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

    效果图如下

     

      

     

     

  • 相关阅读:
    时间日期date/cal
    chown命令
    su命令
    which命令和bin目录
    python基础之文件操作
    python之模块之shutil模块
    python基础之面向对象01
    python基础之面向对象02
    python基础之map/reduce/filter/sorted
    python基础之模块之序列化
  • 原文地址:https://www.cnblogs.com/neverMore-face/p/10154792.html
Copyright © 2020-2023  润新知