前言
前面的基础文章列表
- iOS-零基础学习OpenGL ES入门教程(一)
- iOS-OpenGL ES入门教程(二)最简单的纹理Demo
- iOS-OpenGL ES入门教程(三)纹理取样,混合,多重纹理
下面来讲一下光照
光照
先直观看下使用3D灯光模拟技术和不使用的对比图
可以看到使用灯光模拟会让图形更加立体真实。
计算机模拟光照的通俗原理:GPU为每个三角形的顶点进行光线计算,再把结果进行插值,得出每个片元的最终颜色。
OpenGL ES的灯光模拟包括:环境光、漫反射光、镜面反射光。如上图所示。
一个渲染三角形中每个光线的组成部分取决于三个互相关联的因素
- 光线的设置
- 三角形相对于光线方向
- 三角形的材质
光线的计算依赖于表面法向量,法向量可以通过矢量积进行计算。
由于表面法向量决定了平面的方向。通过光线和法向量的角度则
可以计算出漫反射光,环境光,镜面反射光的模拟。这里主要是几何部分内容,不做细讲,使用GLkit,系统会内置模拟计算出灯光效果。
OpenGL ES程序为每个顶点指定了单独的法向量,和顶点的位置,纹理坐标一起保存起来,从而实现模拟灯光的效果
如果一个三角形的三个顶点赋予相同的法向量,则叫平面法线。
如果每个顶点使用包含该顶点的平均值,灯光模拟会创建三角形轻微弯曲感,如下图。
#实例Demo
我们做一个Demo来直观的看一下灯光和法向量,依然使用GLkit框架为我们简化步骤。
先看一下demo效果
绿色线是顶点法线,而黄色线是灯光方向。图中可以直观看到法向量随着顶点变化。
下面看下核心代码部分
数据部分
1 | //顶点 |
对应的九个数据顶点。
1 | //8 triangles |
8个数据源三角形,每个三角形有三个顶点。也就是法线24条。每条法线绘制需要起始和终止两个顶点,也就是48个数据源顶点,额外两个顶点用于绘制灯光方向。这里宏定义出来。
属性部分
1 | @interface OpenGLES_LightDemoViewController (){ |
下面列举下矢量的计算函数
给定两个顶点求出法向量函数
1 | //法向量 |
triangle的法向量函数
1 | //triangle的法向量 |
构造triangle
1 | //生成triangle |
如果采用顶点计算法向量,函数如下
1 | //计算8个三角形的法向量,并且赋值更新 |
如果使用顶点所包含的所有三角形的平均法向量,函数计算如下
1 | //更新三角形法向量 顶点采用平均法向量 |
法线和灯光方向顶点数据源update函数
1 | //更新三角形法线 还有灯光方向线 |
虽然计算部分函数比较繁琐,但是相对其实是简单的,因为这一块主要还是线性代数相关的。搞懂了gpu模拟灯光的原理,那么对应计算也就好理解了。
渲染部分代码
1 | - (void)viewDidLoad { |
依然使用GLkit框架的baseEffect帮我们简化灯光操作
light0.diffuseColor和light0.position指定了灯光位置和漫反射颜色。
transform.modelviewMatrix这里是绕着x和z轴做了变换,方便观看,下一章视点会详细讲这里。这里不做多解释
_vertexBufferID 生成三角形的缓存
_extraBufferID 生产法线缓存
同理baseEffect也对应的创建两个,用于绘制不同效果。
1 | - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{ |
绘制部分依然是常规的绘制步骤。这里不做累述。指定指针偏移,绘制。
法线绘制
1 | //绘制法线 |
使用绿色绘制法线。黄色绘制灯光方向。
可以自行代码调节灯光的位置和光线属性,查看对应的效果变化。
小思考:1. 我们仅仅使用了顶点的法向量模拟灯光效果,那么相应的是不是可以给每个片元都缓存法向量呢。这样更加真实。
答:是可以的,这里的偏远计算,在每个RGB纹素编码的过程中加入x,y,z的法向量分量,这样的纹理叫做法线贴图。(或者凹凸贴图,DOT3灯光,这三个名词本质都是一种描述)
灯光烘焙到纹理
同样我们可以把灯光烘焙到纹理中,GPU模拟灯光需要做出的运算量非常大,烘焙到纹理则可以避开模拟灯光的矢量运算。但是相应的光烘焙进纹理仅仅适用于静态场景。在灯光位置会改变,动态场景下显然是不适用的。
Demo代码地址:LearnOpenGLESDemo
源码来源于书籍:1. OpenGL ES应用开发实践指南:iOS卷