学习OpenGL:笔记七
光照
使用Shader实现平行光的效果
平行光的方向不会随着离光源的距离而改变。所以我们在模拟平行光的时候仅仅需要使用一个光照方向即可。 我们有了光照方向,接下来还需要一个重要数据,平面的朝向。一个平面如果刚好面朝光线,那自然是最亮的。当然还有些材质的平面可以反射光线,反射光线的强度和你观察的角度相关,不过这些本文都不会介绍。后面会有专门一篇介绍复杂的光照模型。 我们用法线向量来表示平面朝向,在具体实现中,每个点都会有一个法线向量。所谓法线向量就是垂直于平面的一个三维向量。
每个多边形的每个点有一个法线向量,法线向量应该总是被规范化成单位向量。
有了法线向量和光照方向之后,只要将它们相乘即可得到光照强度。接下来开始分析代码。
两个单位向量相乘,结果是cos(向量夹角),夹角越大,cos(向量夹角)越小,刚好符合前面说的规律。
Vertex Shader:
attribute vec4 position; attribute vec3 normal; //法线向量 uniform float elapsedTime; uniform mat4 projectionMatrix; uniform mat4 cameraMatrix; uniform mat4 modelMatrix; varying vec3 fragNormal;//将前面normal法线向量传递给 fragment shader void main(void) { mat4 mvp = projectionMatrix * cameraMatrix * modelMatrix; fragNormal = normal; gl_Position = mvp * position; }
Fragment Shader:
precision highp float; varying vec3 fragNormal; uniform float elapsedTime; uniform vec3 lightDirection;//光线方向 uniform mat4 normalMatrix;//法线变换矩阵 void main(void) { vec3 normalizedLightDirection = normalize(-lightDirection); vec3 transformedNormal = normalize((normalMatrix * vec4(fragNormal, 1.0)).xyz); float diffuseStrength = dot(normalizedLightDirection, transformedNormal); diffuseStrength = clamp(diffuseStrength, 0.0, 1.0); vec3 diffuse = vec3(diffuseStrength); vec3 ambient = vec3(0.3); vec4 finalLightStrength = vec4(ambient + diffuse, 1.0); vec4 materialColor = vec4(1.0, 0.0, 0.0, 1.0); gl_FragColor = finalLightStrength * materialColor; }
因为光线是照射到平面的方向,而法线是从平面往外的方向,所以他们相乘之前需要把光照方向反过来,并且要规范化
vec3 normalizedLightDirection = normalize(-lightDirection);
法线向量的数据:
- (void)bindAttribs:(GLfloat *)triangleData { // 启用Shader中的两个属性 // attribute vec4 position; // attribute vec4 color; GLuint positionAttribLocation = glGetAttribLocation(self.shaderProgram, "position"); glEnableVertexAttribArray(positionAttribLocation); GLuint colorAttribLocation = glGetAttribLocation(self.shaderProgram, "normal"); glEnableVertexAttribArray(colorAttribLocation); // 为shader中的position和color赋值 // glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr) // indx: 上面Get到的Location // size: 有几个类型为type的数据,比如位置有x,y,z三个GLfloat元素,值就为3 // type: 一般就是数组里元素数据的类型 // normalized: 暂时用不上 // stride: 每一个点包含几个byte,本例中就是6个GLfloat,x,y,z,r,g,b // ptr: 数据开始的指针,位置就是从头开始,颜色则跳过3个GLFloat的大小 glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData); glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData + 3 * sizeof(GLfloat)); }
- (void)drawXPlanes { static GLfloat triangleData[] = { // X轴0.5处的平面 0.5, -0.5, 0.5f, 1, 0, 0, 0.5, -0.5f, -0.5f, 1, 0, 0, 0.5, 0.5f, -0.5f, 1, 0, 0, 0.5, 0.5, -0.5f, 1, 0, 0, 0.5, 0.5f, 0.5f, 1, 0, 0, 0.5, -0.5f, 0.5f, 1, 0, 0, // X轴-0.5处的平面 -0.5, -0.5, 0.5f, 1, 0, 0, -0.5, -0.5f, -0.5f, 1, 0, 0, -0.5, 0.5f, -0.5f, 1, 0, 0, -0.5, 0.5, -0.5f, 1, 0, 0, -0.5, 0.5f, 0.5f, 1, 0, 0, -0.5, -0.5f, 0.5f, 1, 0, 0, }; [self bindAttribs:triangleData]; glDrawArrays(GL_TRIANGLES, 0, 12); } - (void)drawYPlanes { static GLfloat triangleData[] = { -0.5, 0.5, 0.5f, 0, 1, 0, -0.5f, 0.5, -0.5f, 0, 1, 0, 0.5f, 0.5, -0.5f, 0, 1, 0, 0.5, 0.5, -0.5f, 0, 1, 0, 0.5f, 0.5, 0.5f, 0, 1, 0, -0.5f, 0.5, 0.5f, 0, 1, 0, -0.5, -0.5, 0.5f, 0, 1, 0, -0.5f, -0.5, -0.5f, 0, 1, 0, 0.5f, -0.5, -0.5f, 0, 1, 0, 0.5, -0.5, -0.5f, 0, 1, 0, 0.5f, -0.5, 0.5f, 0, 1, 0, -0.5f, -0.5, 0.5f, 0, 1, 0, }; [self bindAttribs:triangleData]; glDrawArrays(GL_TRIANGLES, 0, 12); } - (void)drawZPlanes { static GLfloat triangleData[] = { -0.5, 0.5f, 0.5, 0, 0, 1, -0.5f, -0.5f, 0.5, 0, 0, 1, 0.5f, -0.5f, 0.5, 0, 0, 1, 0.5, -0.5f, 0.5, 0, 0, 1, 0.5f, 0.5f, 0.5, 0, 0, 1, -0.5f, 0.5f, 0.5, 0, 0, 1, -0.5, 0.5f, -0.5, 0, 0, 1, -0.5f, -0.5f, -0.5, 0, 0, 1, 0.5f, -0.5f, -0.5, 0, 0, 1, 0.5, -0.5f, -0.5, 0, 0, 1, 0.5f, 0.5f, -0.5, 0, 0, 1, -0.5f, 0.5f, -0.5, 0, 0, 1, }; [self bindAttribs:triangleData]; glDrawArrays(GL_TRIANGLES, 0, 12); }
准备一个平行光,让它向下
self.lightDirection = GLKVector3Make(0, -1, 0);
最后给uniform光照方向和法线变换矩阵赋值
bool canInvert; GLKMatrix4 normalMatrix = GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert); if (canInvert) { GLuint modelMatrixUniformLocation = glGetUniformLocation(self.shaderProgram, "normalMatrix"); glUniformMatrix4fv(modelMatrixUniformLocation, 1, 0, normalMatrix.m); } GLuint lightDirectionUniformLocation = glGetUniformLocation(self.shaderProgram, "lightDirection"); glUniform3fv(lightDirectionUniformLocation, 1,self.lightDirection.v);
效果图如下
每天进步一点点。