学习OpenGL ES之基本光照

news/2024/6/27 7:28:31

获取示例代码


本文主要介绍如何使用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;

void main(void) {
    mat4 mvp = projectionMatrix * cameraMatrix * modelMatrix;
    fragNormal = normal;
    gl_Position = mvp * position;
}
复制代码

我将attribute vec4 color;换成了attribute vec3 normal;,不再传递颜色数据,改为法线向量。然后将法线向量传递给Fragment ShaderfragNormal = normal;

接下来是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;
}
复制代码

我增加了光线方向uniform vec3 lightDirection;,法线变换矩阵uniform mat4 normalMatrix;

法线不能直接使用modelMatrix进行变换,需要使用modelMatrix的逆转置矩阵,参考维基百科

因为光线是照射到平面的方向,而法线是从平面往外的方向,所以他们相乘之前需要把光照方向反过来,并且要规范化。

vec3 normalizedLightDirection = normalize(-lightDirection);
复制代码

接着我们将法线变换后再规范化,我们就得到了关键的两个向量。下面是示意图。

将它们相乘最后得到 diffuse,可以称它为漫反射强度。漫反射就是投射在粗糙表面上的光向各个方向反射的现象。我们求解 diffuse就是模拟的漫反射现象。 代码最后还有一个 vec3 ambient = vec3(0.3);是什么呢?根据漫反射的公式,总会有强度为0的地方,为了使场景不那么暗,就增加了一个基本光照强度,也可称为环境光强度。 环境光强度加上漫反射强度就是最后的光照强度 finalLightStrength了。光照强度乘以材质本身的颜色 materialColor得到最终的颜色,这里材质本身的颜色我用的是红色。

看完Shader,我们回到OC代码。首先,我将绑定Shader属性color的代码改为了绑定normal。

- (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);
}
复制代码

以X轴上的两个平面为例,X轴0.5处的平面法线方向是X轴正向,X轴-0.5处的平面法线方向是X轴反向。这样我们才能让朝外的面接收到光线。法线是三维向量,所以刚好填满了之前颜色的数据区间。

下一步准备一个三维向量存放光照的方向。

@property (assign, nonatomic) GLKVector3 lightDirection; // 平行光光照方向
复制代码

并给它赋值。让它向下照射,所以向量为-Y轴(0,-1,0)。

// 设置平行光方向
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);
复制代码

这里我们使用了GLKit的GLKMatrix4InvertAndTranspose计算modelMatrix的逆转置矩阵,然后传递给Shader。传递光照方向时使用glUniform3fv来传递三维数组。

到此,基本的平行光光照模型就完成了。下面是效果图。

下一篇是基础篇的最后一篇,介绍纹理的加载和使用。后续会在进阶篇中介绍高级光照,3D模型加载,粒子系统等更深入的知识。


http://www.niftyadmin.cn/n/3743481.html

相关文章

tensorflow笔记:流程,概念和简单代码注释

1.tensorflow的运行流程 tensorflow的运行流程主要有2步,分别是构造模型和训练。 在构造模型阶段,我们需要构建一个图(Graph)来描述我们的模型。所谓图,也可以理解为流程图,就是将数据的输入->中间处理->输出的过程表示出来…

计算机应用基础1000,计算机应用基础-在线作业_B 最终成绩1000.doc

计算机应用基础-在线作业_B 最终成绩1000计算机应用基础-在线作业_B最终成绩:100.0一 单项选择题1.______。计算机辅助教学(CAI)计算机支持协同工作视频会议系统娱乐和游戏本题分值: 5.0用户得分: 5.0用户解答: 计算机支持协同工作…

采用select2框架重置功能解决方案

2019独角兽企业重金招聘Python工程师标准>>> 前端采用select2框架后,好多select的方法都不起作用了 我的select2生成的源码如下 需求:每个jsp页面查询条件有多个select元素,点击重置(清空条件内容)select元…

计算机管理员用户被禁用,Windows 10 账户没有了管理员权限或唯一的管理员账户被禁用了,怎么办!...

技术级别 : 基本摘要由于某些原因,导致当前用户账户被设置为非管理员账户。或者在运行程序时提示要输入管理员密码,,但却没地方能够输入。这类情况下,需要进入安全模式重新设定用户账户权限。适用于: Windows 10详细信息第一步点击…

html怎么让input透明,html如何让input隐藏

隐藏方法:1、设置input标签 type属性的值为“hidden”;2、利用input标签的style属性,添加“display:none”样式;3、利用input标签的style属性,添加“visibility:hidden”样式。本教程操作环境:windows7系统…

MS复制学习笔记(新数据库)

MS复制 环境:master原来没有旧数据 1.master和slave都关闭防火墙#systemctl stop firewalld#setenforce 0 master Mysql 修改配置文件 #vim /etc/my.cnf[ ]log-binmylogserver-id1 创建账户 mysq> grant replication slave,reload,super on *.* to slave% identi…

我的未来作文计算机行业,我的未来职业畅想作文

我的未来职业畅想作文畅想是无拘无束地尽情想象。小编为大家搜集整理的我的未来职业畅想为题的作文,欢迎大家阅读与借鉴,希望能够给你带来帮助。我的未来职业畅想作文【1】梦想,是全国人民共同拥有的东西。黑暗中,一盏照亮前进的灯…

车站广播系统采用计算机,高铁站广播系统

高铁站自动广播系统是消防紧急广播与业务广播合二为一的广播系统,在平时作为车站业务广播使用,在有火灾报警信号时,切换为消防广播使用。在业务广播时,主要由自动广播及人工广播组成。高铁站公共广播系统的车次信息源来自信息集成…