您好,登錄后才能下訂單哦!
經過前面的關于GLSL基礎學習,可以參考OpenGL進階專欄中的一些文章。
接下來的內容將會非常的有意思,比如全局光照,環境貼圖,法線貼圖,景深....是不是聽著就很棒!想要學習的話,當然需要付出更多的努力!不過一切都是值得的。
今天的內容主要是關于光照的一些基礎著色。
在光照模型中,光照主要由三部分組成:環境光(ambient),漫反射(diffuse),鏡面反射(specular)。環境光是那些在環境中進行充分的散射,無法分辨方向的光,它似乎時來自所有方向的。漫反射來自某個方向,因此,如果它從正面照射表面,它看起來顯得更亮一些,反之,如果它是斜著掠過表面,它看起來就顯得暗一些,當它撞擊到物體的表面的時候,它會均勻地向所有的方向發散。鏡面光來自一個特定的方向,并且傾向于從表面某個特定的方向反射,當有一束激光從一面鏡子上反彈回來時,它所產生的幾乎是百分百的鏡面反射光。
關于光照的原理及編程實現,可以參考另一篇文章:光線追蹤(RayTracing)算法理論與實踐(三)光照
這里需要用到一個模型,來自stanford的兔子 - bunny,在文章的最后有下載,最好自己加載到blender里面再導出一遍,不然似乎沒有法線信息。
程序實現的思路:
1)加載obj模型。
2)將頂點法線坐標存到VBO中,并作為參數傳到shader中去。
3)在shader中定義關于光照以及MVP的Uniform變量。
4)在程序中設置uniform變量。
5)在定點shader中計算頂點位置和光照強度,在片段shader中對片段進行著色。
具體的代碼實現如下。
首先在代碼中添加一個ObjObject類,用來表示Obj模型類。
objobject.h
#ifndef OBJOBJECT_H #define OBJOBJECT_H #include "util.h" class ObjObject { public: ObjObject(); ObjObject(const char * path); virtual ~ObjObject(); int getVerticesCount(); vector<glm::vec3> vertices; vector<glm::vec2> uvs; vector<glm::vec3> normals; protected: private: }; #endif // TEAPOT_H
#include "objobject.h" ObjObject::ObjObject() { //ctor } ObjObject::~ObjObject() { //dtor } ObjObject::ObjObject(const char * path) { Util u; u.loadOBJ(path, this->vertices, this->uvs, this->normals); } int ObjObject::getVerticesCount() { return this->vertices.size(); }
在initGL中對bunny進行初始化:
bunny = ObjObject("bunny.obj");
void CGL::compileShader() { glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, bunny.vertices.size() * sizeof(glm::vec3), &bunny.vertices[0], GL_STATIC_DRAW); //glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), positionData, GL_STATIC_DRAW); // 1rst attribute buffer : vertices glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer( 0, // attribute 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); GLuint normalbuffer; glGenBuffers(1, &normalbuffer); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glBufferData(GL_ARRAY_BUFFER, bunny.normals.size() * sizeof(glm::vec3), &bunny.normals[0], GL_STATIC_DRAW); // 3rd attribute buffer : normals glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glVertexAttribPointer( 1, // attribute 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); if( ! prog.compileShaderFromFile("shader/basic1.vert",GLSLShader::VERTEX) ) { printf("Vertex shader failed to compile!\n%s", prog.log().c_str()); exit(1); } if( ! prog.compileShaderFromFile("shader/basic1.frag",GLSLShader::FRAGMENT)) { printf("Fragment shader failed to compile!\n%s", prog.log().c_str()); exit(1); } prog.bindAttribLocation(0, "VertexPosition"); prog.bindAttribLocation(1, "VertexNormal"); if( ! prog.link() ) { printf("Shader program failed to link!\n%s", prog.log().c_str()); exit(1); } if( ! prog.validate() ) { printf("Program failed to validate!\n%s", prog.log().c_str()); exit(1); } prog.use(); }
void CGL::setUniform() { mat4 model = mat4(1.0f); //model *= glm::rotate(model, -35.0f, vec3(1.0f,0.0f,0.0f)); // model *= glm::rotate(model, 35.0f, vec3(0.0f,1.0f,0.0f)); mat4 view = glm::lookAt(vec3(0.0f,5.0f,10.0f), vec3(-1.0f,2.0f,0.0f), vec3(0.0f,1.0f,0.0f)); mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f); mat4 mv = view * model; prog.setUniform("Kd", 0.6f, 0.9f, 0.9f); prog.setUniform("Ld", 1.0f, 1.0f, 1.0f); prog.setUniform("LightPosition", view * vec4(-5.0f,20.0f,15.0f,1.0f) ); prog.setUniform("ModelViewMatrix", mv); prog.setUniform("NormalMatrix",mat3( vec3(mv[0]), vec3(mv[1]), vec3(mv[2]) )); prog.setUniform("MVP", projection * mv); }
#version 400 layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; out vec3 LightIntensity; uniform vec4 LightPosition; // Light position in eye coords. uniform vec3 Kd; // Diffuse reflectivity uniform vec3 Ld; // Diffuse light intensity uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 MVP; void main() { vec3 tnorm = normalize(NormalMatrix * VertexNormal); vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition, 1.0); vec3 s = normalize(vec3(LightPosition - eyeCoords)); LightIntensity = Ld * Kd * max(dot(s,tnorm),0.0); gl_Position = MVP * vec4( VertexPosition, 1.0); }
#version 400 in vec3 LightIntensity; out vec4 gl_FragColor; void main(void) { gl_FragColor = vec4(LightIntensity, 1.0); }
glDrawArrays(GL_TRIANGLES, 0, bunny.vertices.size() );
void CGL::clean() { glDeleteBuffers(1, &vertexbuffer); prog.deleteProgram(); glDeleteVertexArrays(1, &VertexArrayID); }
ADS就是上面提到的光照模型,也稱做Phong reflection model 或者 Phong Shading model。這時候光照的計算模型就是:
主要需要修改的是頂點shader,在著色器中實現對光照效果的計算:
basic.vert
#version 400 layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; out vec3 LightIntensity; struct LightInfo{ vec4 Position; vec3 La; vec3 Ld; vec3 Ls; }; struct MaterialInfo{ vec3 Ka; vec3 Kd; vec3 Ks; float Shininess; }; uniform LightInfo Light; uniform MaterialInfo Material; uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 ProjectionMatrix; uniform mat4 MVP; void getEyeSpace(out vec3 norm, out vec4 position) { norm = normalize(NormalMatrix * VertexNormal); position = ModelViewMatrix * vec4(VertexPosition, 1.0); } vec3 phongModel(vec4 position, vec3 norm) { vec3 s = normalize(vec3(Light.Position - position)); vec3 v = normalize(-position.xyz); vec3 r = reflect(-s, norm); vec3 ambient = Light.La * Material.Ka; float sDotN = max(dot(s, norm), 0.0); vec3 diffuse = Light.Ld * Material.Kd * sDotN; vec3 spec = vec3(0.0); if(sDotN >0.0) spec = Light.Ls * Material.Ks * pow(max(dot(r,v), 0.0), Material.Shininess); return ambient + diffuse + spec; } void main() { vec3 eyeNorm; vec4 eyePosition; getEyeSpace(eyeNorm, eyePosition); LightIntensity = phongModel(eyePosition, eyeNorm); gl_Position = MVP * vec4( VertexPosition, 1.0); //gl_Position = vec4( VertexPosition, 1.0); }
程序中只要設置好相關的uniform就可以了:
void CGL::setUniform() { mat4 model = mat4(1.0f); mat4 view = glm::lookAt(vec3(0.0f,5.0f,10.0f), vec3(-1.0f,2.0f,0.0f), vec3(0.0f,1.0f,0.0f)); mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f); mat4 mv = view * model; prog.setUniform("Material.Kd", 0.9f, 0.5f, 0.3f); prog.setUniform("Light.Ld", 1.0f, 1.0f, 1.0f); prog.setUniform("Material.Ka", 0.9f, 0.5f, 0.3f); prog.setUniform("Light.La", 0.4f, 0.4f, 0.4f); prog.setUniform("Material.Ks", 0.8f, 0.8f, 0.8f); prog.setUniform("Light.Ls", 1.0f, 1.0f, 1.0f); prog.setUniform("Material.Shininess", 100.0f); prog.setUniform("ModelViewMatrix", mv); prog.setUniform("NormalMatrix",mat3( vec3(mv[0]), vec3(mv[1]), vec3(mv[2]) )); prog.setUniform("MVP", projection * mv); }
由于關于著色的計算是在頂點shader中完成的,所以也可以稱為逐頂點著色(per-vertex lighting)。
當渲染的模型是完全封閉的時候,模型中所有面的背面都是不可見的,但是,如果模型有開口的話就比較麻煩了,渲染的結果很可能并不正確,因為面的法線并不正確。這時候就需要將法線反向,然后根據反向后的法線來計算光強。
直接渲染帶洞的model,結果如下:
修改一下shader:
basic.vert
#version 400 layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; //out vec3 LightIntensity; out vec3 frontColor; out vec3 backColor; struct LightInfo{ vec4 Position; vec3 La; vec3 Ld; vec3 Ls; }; struct MaterialInfo{ vec3 Ka; vec3 Kd; vec3 Ks; float Shininess; }; uniform LightInfo Light; uniform MaterialInfo Material; uniform mat4 ModelViewMatrix; uniform mat3 NormalMatrix; uniform mat4 ProjectionMatrix; uniform mat4 MVP; void getEyeSpace(out vec3 norm, out vec4 position) { norm = normalize(NormalMatrix * VertexNormal); position = ModelViewMatrix * vec4(VertexPosition, 1.0); } vec3 phongModel(vec4 position, vec3 norm) { vec3 s = normalize(vec3(Light.Position - position)); vec3 v = normalize(-position.xyz); vec3 r = reflect(-s, norm); vec3 ambient = Light.La * Material.Ka; float sDotN = max(dot(s, norm), 0.0); vec3 diffuse = Light.Ld * Material.Kd * sDotN; vec3 spec = vec3(0.0); if(sDotN >0.0) spec = Light.Ls * Material.Ks * pow(max(dot(r,v), 0.0), Material.Shininess); return ambient + diffuse + spec; } void main() { vec3 eyeNorm; vec4 eyePosition; getEyeSpace(eyeNorm, eyePosition); frontColor = phongModel(eyePosition, eyeNorm); backColor = phongModel(eyePosition, -eyeNorm); gl_Position = MVP * vec4( VertexPosition, 1.0); //gl_Position = vec4( VertexPosition, 1.0); }
#version 400 //in vec3 LightIntensity; in vec3 frontColor; in vec3 backColor; out vec4 gl_FragColor; void main(void) { if(gl_FrontFacing) gl_FragColor = vec4(frontColor, 1.0); else gl_FragColor = vec4(backColor, 1.0); }
這個著色方式是想對于Gouraud Shading (高洛德著色/高氏著色)來說的。
Gouraud Shading 在游戲中使用最廣泛的一種著色方式。它可對3D模型各頂點的顏色進行平滑、融合處理,將每個多邊形上的每個點賦以一組色調值,同時將多邊形著上較為順滑的漸變色,使其外觀具有更強烈的實時感和立體動感,不過其著色速度比平面著色慢得多。
在shader中要實現flat shading非常簡單,只要在 in out 參數的前面加上flat關鍵字就可以了。
baisic .vert
...... flat out vec3 LightIntensity; ....
... flat in vec3 LightIntensity; ...
Stanford dragon
Stanford bunny
程序源碼
OpenGL 4.0 Shading Language Cookbook
OpenGL 編程指南
OpenGL Shading Language
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。