Abstract
After the previous glsl basic learning, you can refer to some articles in the OpenGL advanced column.
The following content will be very interesting, such as Global illumination, Environment texture, normal texture, depth of field... Is it great to listen! If you want to learn, you must make more effort! But everything is worth it.
Today's content is mainly about some basic coloring of light.
In the illumination model, illumination mainly consists of three parts: ambient, diffuse, and specular ).Ambient LightIt is the light that is fully scattered in the environment and cannot be distinguished from the direction. It seems to come from all directions. The diffuse reflection comes from a certain direction. Therefore, if it is shining from the front of the surface, it looks brighter. Otherwise, if it is oblique over the surface, it looks darker, when it hits the surface of an object, it spreads evenly to all directions. The mirror light comes from a specific direction and tends to be reflected in a specific direction on the surface. When a laser returns from a specific side of the mirror, it produces almost of the mirror light.
For the principle and programming implementation of illumination, refer to another article: raytracing Algorithm Theory and Practice (iii) Illumination
Diffuse Reflection coloring under single-point illumination
Here we need to use a model, rabbit bunny from Stanford, which has been downloaded at the end of the article. We 'd better load it into the blender and export it again, otherwise it seems that there is no normal information.
Program Implementation ideas:
1) load the OBJ model.
2) store the vertices line coordinates in VBO and pass them to the shader as parameters.
3) define the illumination and MVP uniform variables in the shader.
4) set the uniform variable in the program.
5) Calculate the vertex position and illumination intensity in the fixed-point shader and color the segment in the segment shader.
The specific code implementation is as follows.
First, add an objobject class to the Code to represent the OBJ model class.
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
Objobject. cpp
#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();}
The next step is to implement the steps mentioned above step by step in rendering.
Initialize bunny in initgl:
bunny = ObjObject("bunny.obj");
Then generate VAO and VBO corresponding to the fixed point and normal, and load shader
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 : verticesglEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);glVertexAttribPointer(0, // attribute3, // sizeGL_FLOAT, // typeGL_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 : normalsglEnableVertexAttribArray(1);glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);glVertexAttribPointer(1, // attribute3, // sizeGL_FLOAT, // typeGL_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();}
Set the uniform variable:
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);}
Vertex shader:
#version 400layout (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 reflectivityuniform vec3 Ld; // Diffuse light intensityuniform 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);}
Segment shader:
#version 400in vec3 LightIntensity;out vec4 gl_FragColor;void main(void){gl_FragColor = vec4(LightIntensity, 1.0);}
Finally, rendering:
glDrawArrays(GL_TRIANGLES, 0, bunny.vertices.size() );
When jumping out of the rendering loop, do not forget to delete the cache content:
void CGL::clean(){ glDeleteBuffers(1, &vertexbuffer);prog.deleteProgram();glDeleteVertexArrays(1, &VertexArrayID);}
The running result is as follows:
ADS coloring (ambientdiffusespecular shading)
ADS is the lighting model mentioned above, also called Phong reflection model or Phong Shading Model. The computing model of illumination is:
The vertex shader needs to be modified to calculate the illumination effect in the shadow:
Basic. Vert
#version 400layout (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;uniformMaterialInfo 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);}
Note that the function is used in the shader, which is very similar to the C language, but the definition of the return value is somewhat different and cannot be repeated, even in different scopes.
In the program, you only need to set the relevant 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);}
Rendering.
Because the coloring calculation is completed in the vertex shader, it can also be calledColor by vertex (Per-vertex lighting).
Two-sided Shading
When the rendered model is completely closed, the back of all aspects of the model are invisible. However, if the model has an opening, the rendering results may be incorrect, because the face's normal is incorrect. At this time, we need to reverse the normal, and then calculate the Light Intensity Based on the Inverse normal.
Directly render the model with holes. The result is as follows:
Modify the shader:
Basic. Vert
#version 400layout (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;uniformMaterialInfo 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);}
Basic. Frag
#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);}
Calculate the color of the internal and external segments in basic. Vert, then judge whether the surface is the back of the segment based on gl_frontfacing in the segment coloring tool, and then color the surface separately and render it.
Flat Shading
This coloring method is intended for Gouraud shading.
Gouraud shading is the most widely used coloring method in games. It can smooth and combine the colors of vertices in the 3D model, assign each vertex on each polygon to a set of color values, and place the Polygon on a smoother gradient color, make its appearance more real-time and three-dimensional dynamic, but its coloring speed is much slower than that of plane coloring.
To implement flat shading in the shader, you only need to add the flat keyword before the in out parameter.
Baisic. Vert
......flat out vec3 LightIntensity;....
Basic. Frag
...flat in vec3 LightIntensity;...
Render and compare the two effects:
Related downloads
Stanford Dragon
Stanford bunny
Program source code
Reference
OpenGL 4.0 shading language cookbook
OpenGL programming guide
OpenGL shading Language