学习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);

效果图如下

 

  

 

 

 

posted @ 2018-12-21 11:29  _NeverMore  阅读(222)  评论(0编辑  收藏  举报