OpenGL进阶(十三) - GLSL光照(Lighting)
提要
在上一篇文章中,我们介绍了简单的Shading,同时提出了一个光照模型,模拟了一个点光源,但是,关于光的故事还没有结束...
今天要学习的是方向光源(Directional Light),聚光灯,per pixel shading,halfway vector。
关于光源的原理及数学描述,请参考:光线追踪(RayTracing)算法理论与实践(三)光照
方向光源
方向光源就两个参数,方向和强度。
还是简单的 ambient + diffuse + spec 光照模型。先看shader的代码。
basic.vert
#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal; out vec3 LightIntensity;struct LightInfo{vec4 Direction;vec3 Intensity;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform LightInfo Light;
uniform MaterialInfo Material;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm = normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}vec3 ads(vec4 position, vec3 norm)
{vec3 s;if(Light.Direction.w == 0.0)s = normalize(vec3(Light.Direction));elses = normalize(vec3(Light.Direction - position));vec3 v = normalize(vec3(-position));vec3 r = reflect(-s, norm);return Light.Intensity * (Material.Ka + Material.Kd*max(dot(s,norm), 0.0) + Material.Ks * pow(max(dot(r,v),0.0), Material.Shininess));
}void main()
{vec3 eyeNorm;vec4 eyePosition;getEyeSpace(eyeNorm, eyePosition);LightIntensity = ads(eyePosition, eyeNorm);gl_Position = MVP * vec4( VertexPosition, 1.0);
}
在ads函数中,首先通过nomal矩阵将顶点法向量变换到视口坐标下,(nomal矩阵其实就是model-view矩阵的左上3x3的矩阵)然后通过model-view矩阵将顶点坐标转化为视口坐标系(eye coordinates)下。
接下来的ads用来计算光照模型下顶点的颜色,分别计算三个分量,然后相加。
basic.frag
#version 400in vec3 LightIntensity;void main(void)
{gl_FragColor = vec4(LightIntensity, 1.0);//gl_FragColor = vec4(1.0,1.0,0.1, 1.0);
}
这个就是将根据顶点shader传来的颜色对片段进行赋值。
void CGL::setUniform()
{mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);mat4 mv = view * model;prog.setUniform("Material.Kd", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ka", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ks", 0.8f, 0.8f, 0.8f);prog.setUniform("Material.Shininess", 100.0f);prog.setUniform("Light.Direction", vec4(1.0f, 0.0f, 0.0f, 0.0f));prog.setUniform("Light.Intensity", 1.0f, 1.0f, 1.0f);prog.setUniform("ModelViewMatrix", mv);prog.setUniform("NormalMatrix",mat3( vec3(mv[0]), vec3(mv[1]), vec3(mv[2]) ));prog.setUniform("MVP", projection * mv);}
渲染的效果如下:
halfway vector 性能优化
在前面的光照模型中,用于计算specular分量的公式如下:
其中 r 是反射光线的方向向量,v是往视口方向的向量,其中 r 的计算:
这个计算过程会非常耗时,我们可以用一个trick来改善一下。
定义一个 h (halfway vector)向量:
下图是 h 和其他向量的位置关系。
specular分量的计算就可以转化成:
相比于计算 r ,h 的计算相对比较简单,而 h 和 n 之间的夹角与 v 和 r 之间的夹角大小几乎相同!那就意味着我们可以用 h.n 来代替 r.v 从而可以带利用 halfway vector 来获得性能上的一些提升。虽然效果上会有那么小小的不同。
后面的灯光的计算都会用到这个优化。
聚光灯 Spotlight
这里的采用一个最简单的聚光灯模型:
灯光的属性有:位置,强度,方向,衰减(exponent),裁剪角度。
实现起来也比较简单,在投射角内的物体,渲染方式和点光源的计算一样,投射角之外的顶点,着色的时候只有ambient。
还是采用我们比较熟悉的per vertex shading 方式。在vert中定义聚光灯:
//baisc.vert
#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal; out vec3 LightIntensity;struct SpotLightInfo{vec4 position;vec3 direction;vec3 intensity;float exponent;float cutoff;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform SpotLightInfo Spot;
uniform MaterialInfo Material;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm = normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}vec3 adsSpotLight(vec4 position, vec3 norm)
{vec3 s = normalize(vec3(Spot.position - position));float angle = acos(dot(-s, normalize(Spot.direction)));float cutoff = radians(clamp(Spot.cutoff, 0.0, 90.0));vec3 ambient = Spot.intensity * Material.Ka;if(angle < cutoff){float spotFactor = pow(dot(-s, normalize(Spot.direction)), Spot.exponent);vec3 v = normalize(vec3(-position));vec3 h = normalize(v + s);return ambient + spotFactor * Spot.intensity * (Material.Kd * max(dot(s, norm),0.0)+ Material.Ks * pow(max(dot(h,norm), 0.0),Material.Shininess)); }else{return ambient; }}void main()
{vec3 eyeNorm;vec4 eyePosition;getEyeSpace(eyeNorm, eyePosition);LightIntensity = adsSpotLight(eyePosition, eyeNorm);gl_Position = MVP * vec4( VertexPosition, 1.0);
}
几个GLSL中的内置函数在这里说明一下。
genType clamp( genType x, genType minVal, genType maxVal);
获取三个数中第二大的数。
genType radians(genType degrees);
将角度转换成弧度。
adsSpotLight是主要的函数,先计算顶点和光源方向之间的夹角,判断顶点是否在照射的区域,然后分别求得最终的颜色。
片段shader还是那样:
#version 400in vec3 LightIntensity;void main(void)
{gl_FragColor = vec4(LightIntensity, 1.0);
}
uniform变量的赋值:
void CGL::setUniform()
{//model = glm::rotate(this->model, 10.0f, vec3(0.0f,1.0f,0.0f));mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);mat4 mv = view * model;mat3 normalMatrix = mat3( vec3(view[0]), vec3(view[1]), vec3(view[2]) );prog.setUniform("Material.Kd", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ka", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ks", 0.8f, 0.8f, 0.8f);prog.setUniform("Material.Shininess", 100.0f);vec4 lightPos = vec4(1.0f, 5.0f, 20.0f, 1.0f);// prog.setUniform("Spot.position", lightPos);prog.setUniform("Spot.position", view * lightPos);prog.setUniform("Spot.direction", normalMatrix * vec3(-10.0,0.0,-40.0) );//prog.setUniform("Spot.direction", vec3(10.9f,10.9f,10.9f) );prog.setUniform("Spot.intensity", vec3(0.9f,0.9f,0.9f) );prog.setUniform("Spot.exponent", 30.0f );prog.setUniform("Spot.cutoff", 15.0f );prog.setUniform("ModelViewMatrix", mv);prog.setUniform("NormalMatrix",normalMatrix);prog.setUniform("MVP", projection * mv);}
在这里给Spot.position赋值的时候不是 prog.setUniform("Spot.position", lightPos); 而是prog.setUniform("Spot.position", view * lightPos),因为在shader中的计算都是在视口坐标下进行的,这样做是为了统一坐标。Spot.direction的赋值也是一样。也可以把坐标转换这一步放到shader中去做。
最终效果如下:
逐像素着色 per pixel shading
相对与前面将主要计算工作放在顶点shader中的 per vertex shading ,per pixel shading 指的是将计算放到片段shader中,这样可以带来更加真实可感的渲染效果。
basic2.vert
#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal; out vec4 Position;
out vec3 Normal;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm = normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}void main()
{getEyeSpace(Normal, Position);gl_Position = MVP * vec4( VertexPosition, 1.0);
}
basic2.frag
#version 400in vec4 Position;
in vec3 Normal;struct SpotLightInfo{vec4 position;vec3 direction;vec3 intensity;float exponent;float cutoff;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform SpotLightInfo Spot;
uniform MaterialInfo Material;vec3 adsSpotLight(vec4 position, vec3 norm)
{vec3 s = normalize(vec3(Spot.position - position));float angle = acos(dot(-s, normalize(Spot.direction)));float cutoff = radians(clamp(Spot.cutoff, 0.0, 90.0));vec3 ambient = Spot.intensity * Material.Ka;if(angle < cutoff){float spotFactor = pow(dot(-s, normalize(Spot.direction)), Spot.exponent);vec3 v = normalize(vec3(-position));vec3 h = normalize(v + s);return ambient + spotFactor * Spot.intensity * (Material.Kd * max(dot(s, norm),0.0)+ Material.Ks * pow(max(dot(h,norm), 0.0),Material.Shininess)); }else{return ambient; }}void main(void)
{gl_FragColor = vec4(adsSpotLight(Position, Normal), 1.0);//gl_FragColor = vec4(1.0,1.0,0.5, 1.0);
}
看一下渲染效果:
最终的效果还是有一些差别,特别是光线交界的地方。
代码下载
OpenGLPro13
参考
OpenGL 4.0 Shading Language Cookbook
转载于:https://www.cnblogs.com/bbsno1/p/3279753.html
OpenGL进阶(十三) - GLSL光照(Lighting)相关推荐
- OpenGL ES on iOS --- 光照进阶
OpenGL ES on iOS --- 光照进阶 简述 本文记录我记录我学习 坐标体系和矩阵转换的过程,加深学习便于后续查询,可能有些描述不够准确,或者内容不够充实,还请多多指正,共同学习. 光源分 ...
- 高质量实时渲染课程笔记(二)——图形学基础回顾(渲染管线、OpenGL入门、GLSL、渲染方程)
文章目录 1 图形渲染管线 2 OpenGL 2.1 使用OpenGL过程的比喻: 油画过程 2.2 Place objects/models 放这些模型 模型这么摆放 2.3 Set up an e ...
- CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果...
CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果 开始 一图抵千言.首先来看鼠标拖动太阳(光源)的情形. 然后是鼠标拖拽旋转模型的情形. 然后我们移动摄像 ...
- C++ Opengl纹理过滤和光照实例源码
C++ Opengl纹理过滤和光照实例源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8. ...
- OpenGL ES之GLSL实现仿抖音“缩放”“灵魂出窍”“抖动”“闪白”“毛刺”“幻觉”等动态滤镜效果
无滤镜效果 "动态滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分 ...
- OpenGL ES之GLSL实现仿抖音“灰度滤镜”和“颠倒滤镜”效果
无滤镜 "无滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分内容, ...
- OpenGL基础45:光照矫正(下)之Gamma校正
接上文:OpenGL基础44:光照矫正(上) 四.Gamma矫正 4.1.人的视觉特性 和很多错视图一样,对于下面这张灰阶图,如果1表示纯白,0表示纯黑,那么这张图片的哪个位置代表的是0.5,也就是自 ...
- OpenGL ES之GLSL实现多种“马赛克滤镜”效果
⻢赛克效果 "⻢赛克效果"就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示,可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来. 无马赛克滤镜 "无滤镜& ...
- 【OpenGL】笔记十三、光照贴图
1. 流程 简单的光照材质无法满足要求,接下来我们要对不同材质的物体在各个片段的反射做出精准控制 1.1 漫反射贴图 之前,我们对物体的环境光和漫反射做了简单的单色定义,现在我们加入纹理贴图,让纹理的 ...
最新文章
- php合并数组中相同的元素
- 用Vue框架和后台请求的时候传递的参数的方式
- java快递柜系统开发_他专注智能柜系统开发,产品日使用频次达60万次,服务近400厂家...
- 解決 centos -bash: vim: command not found
- 【转】 ID,ClientID和UniqueID
- 怎能错过这个技术集市!转发有奖,惊喜连连!
- linux和android学习,android学习笔记
- SGU 325 Palindrome(贪心)
- 如何删除 macOS 压缩包中的隐藏文件?
- 就是要你懂Java中volatile关键字实现原理
- IDEA设置背景与字体大小
- 大数据入门的知识体系,大数据学习路线
- 一台变两台,电脑也分身
- win7安装中文语言包
- 双非本科地信前端面试题目
- 管理好你的20~30岁
- mininet 主机双网关拓扑设计
- 基于标定板的手眼标定
- matplotlib之pyplot教程
- 国内短信服务商厂商比较