步兵 shader 基礎篇

2021-08-06 03:30:29 字數 4419 閱讀 7758

引用上文連線中的圖,渲染是乙個線性流程,他是在不停各個環節處理接收到的資料,然後輸出。

首先建立一堆頂點資料,然後頂點著色器對處理這些資料,輸出給圖元裝配器連線成圖形,

然後在輸出給幾何著色器可對圖形進行處理,再光柵化成畫素模式,輸出給片段著色器

計算每乙個畫素的最終顏色,最後混合疊加輸出到到螢幕上。值得一提的地方:在片段著色器執行之前

會執行裁切(clipping),裁切會丟棄超出你的檢視以外的所有畫素,用來提公升執行效率。

可程式設計的部分設計到圖中藍框標註的頂點著色器幾何著色器片段著色器

當然也可以怎麼輸入就怎麼輸出,不進行處理,這樣就會看到原本的圖形。

反之,則是shader程式設計,要對這些頂點、圖形、片段進行處理,顯示出想要的酷炫的效果。

opengl至少需要乙個 vertexshader和fragmentshader,而geometryshader則不是必須的。

而一般的shader處理主要集中在fragmentshader,因為我們很少需要讓圖形形變。

//首先我們需要乙個渲染程式

int shaderprogram = glcreateprogram();

//乙個 vertex shader

int vertexshader = glcreateshader(gl_vertex_shader);

glshadersource(vertexshader, 1, &vertexshadersource, null);

glcompileshader(vertexshader);

//乙個 fragment shader

int fragmentshader = glcreateshader(gl_fragment_shader);

glshadersource(fragmentshader, 1, &fragmentshadersource, null);

glcompileshader(fragmentshader);

//前文也說了,這兩個是必須的

//然後新增到程式上

glattachshader(shaderprogram, vertexshader);

glattachshader(shaderprogram, fragmentshader);

//再進行鏈結,這裡的鏈結就相當與編譯,看你的**是否有語法錯誤、位置變數、或者輸入輸出介面是否同一等

gllinkprogram(shaderprogram);

//編譯完後可以刪除,就想我們只有so就可以執行了一樣

gldeleteshader(vertexshader);

gldeleteshader(fragmentshader);

//最後告訴opengl你使用的是哪個渲染程式

gluseprogram(shaderprogram);

以上是乙個是一渲染程式的建立,其中vertexshadersourcefragmentshadersource

就是我們的shader程式設計的入口,shader程式的原始碼以字串的形式傳入給著色器,就像乙個輕量級指令碼語言。

但是做完這些還不夠,因為沒有涉及到輸入,沒有輸入**來的輸出,下面就是頂點、紋理的建立。

//乙個正方形就需要,這麼多的資料,包含座標、顏色、紋理對映

float vertices = ;

//..頂點資料處理**裡太大,就不貼出來了,有興趣的可以自己去看(麻煩程度,你可能會和我一樣吃驚)

//再就是建立紋理

glbindtexture(gl_texture_2d, texture2);//texture1同理

data = stbi_load(res("awesomeface.png").c_str(), &width, &height, &nrchannels, 0);

if (data)

stbi_image_free(data);//然後釋放掉讀取記憶體

//然後指定多紋理的層級,每個層級對應的引數,傳遞給shader,主要是片段著色器

//因為頂點著色器只涉及定點操作。

gluniform1i(glgetuniformlocation(shaderprogram, "ourtexture1"), 0);

gluniform1i(glgetuniformlocation(shaderprogram, "ourtexture2"),1);

//再接下來就是開始渲染了

//draw loop

//再剩下無非就算一些善後工作,各種銷毀和釋放

可以看到乙個渲染流程並不是想象的那麼簡單,這裡還只是最基本的。

//頂點著色器

//in代表接收到的資料、out代表輸出資料

const

char *vertexshadersource = "#version 330 core\n"

"layout in vec3 apos;\n"

"layout in vec3 acolor;\n"

"layout in vec2 atexcoord;\n"

"out vec3 ourcolor;\n"

"out vec2 texcoord;\n"

"void main()\n"

"\0";

可以看到,頂點並不只代表著位置資訊,還包括顏色和紋理對映,這些都是頂點資訊。

向最開始的笑臉圖那樣,它的漸變是根據4個頂點的顏色數值計算出來的。

gl_position是傳遞給opengl的引數用來確定位置,而ourcolor、texcoord,則是繼續向下傳遞,

因為我們沒有幾何著色器,所以我們的資料直接傳遞給了片段著色器

//片段著色器

const

char *fragmentshadersource = "#version 330 core\n"

"in vec3 ourcolor;\n"

"in vec2 texcoord;\n"

"uniform sampler2d ourtexture;\n"

"uniform sampler2d ourtexture2;\n"

"out vec4 fragcolor;\n"

"void main()\n"

"\n\0";

最後將按照紋理對映,生成兩張紋理,然後混合只保留笑臉的25%的和木箱疊加顏色混合的75%,

從而形成了一開的那張。

到這裡乙個包含shader的基本渲染流程也就講解結束了,自認為還算條理清晰。

不過不結合實際總是不好的,所以我貼乙個結合cocos的最簡單的shader,來強調一下,

shader的基礎知識在那,其他的大同小異。

先上圖:

vs(vertexshader)就是用的cocos自帶的no_mvp,也就是沒有座標系轉換,怎麼輸入就怎麼輸出。

fs(fragmentshader)**如下:

varying

vec4 v_fragmentcolor;//接收到的顏色資訊

varying

vec2 v_texcoord;//接收到的紋理對映

void main(void)

gl_fragcolor = c;

}

很簡單的**,先計算畫素的色值,如果 不透明度大於》0.8,就設定成純白色,最後再輸出。

其實shader的編碼部分都給預留出了介面,輸出也都是固定的,其他的就是輸入,也就是傳參。

靜態的shader基本上就是通過演算法或數學模型對畫素點鄰居的畫素點進行處理,達到各種效果,

動態的shader則是要傳入變值影響計算的結果,讓shader計算結果產生浮動,達到動態效果。

不過並沒有詳細的那麼簡單,可能乙個公式就看不懂直接卡在那,所以shader編**的比我想象中

的要難,因為數學和程式設計結合要比邏輯程式設計難很多,此路漫漫啊~ =、=

see again~

之前 真愛無價,歡迎打賞~

步兵 shader 封裝篇

上篇介紹了基礎,這篇先介紹下shadertools的封裝,為以後打好基礎。注 這篇文章是基於cocos2dx的 文中有不懂的地方可去回顧上篇 步兵 shader 基礎篇 首先介紹幾個類 glprogram shaderprogram glprogramcache 可以存放已經鏈結過的shaderpr...

步兵 shader 掃光

掃光這個需求很常見,一是給介面新增些許生氣,二則是讓介面上的重點突出出來。往往 乙個靜止的介面中 突然有東西動了,就會很容易引起人的注意。掃光的實現方式有很多中,各有優缺點,可能你覺得shader實現是最高大上的,可能在美術眼裡,這種表現效果卻是最low的,所以還是一切以最終要的效果為基準來定製策略...

步兵 經驗篇 one step

前段時間羅永浩我發布會上出了個,這麼個東西,今天說的又更step有關,所以就引用一下。話說沒看過的,不如週末看看這個發布會,我覺得價值遠比手機大,春晚都沒這麼好看,哈哈。但是隨著工作經驗的增加,雖然我可以不用cocos的scene,但是還是來抽象出乙個scene類,用來做資源管理,遊戲中各個階段理好...