引用上文連線中的圖,渲染是乙個線性流程,他是在不停各個環節處理接收到的資料,然後輸出。
首先建立一堆頂點資料,然後頂點著色器對處理這些資料,輸出給圖元裝配器連線成圖形,
然後在輸出給幾何著色器可對圖形進行處理,再光柵化成畫素模式,輸出給片段著色器
計算每乙個畫素的最終顏色,最後混合疊加輸出到到螢幕上。值得一提的地方:在片段著色器執行之前
會執行裁切(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);
以上是乙個是一渲染程式的建立,其中vertexshadersource和fragmentshadersource,
就是我們的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類,用來做資源管理,遊戲中各個階段理好...