CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)...
CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)
2016-08-13
由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了。CSharpGL源码中包含10多个独立的Demo,更适合入门参考。
为了尽可能提升渲染效率,CSharpGL是面向Shader的,因此稍有难度。
光源
如何用GLSL实现点光源和平行光源等各类光源的效果?这个问题我查找资料、思考了很久,今天终于解决了一部分。
漫反射(diffuse reflection)是粗糙表面的反射效果。理论上的粗糙表面,对各个方向的反射效果都完全相同。本篇就分别实现点光源(point light)和平行光源(directional light)照射到粗糙表面时产生的漫反射效果。
下载
这个示例是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
点光源(point light)
讲GLSL当然要从shader开始说起。
Vertex shader
1 #version 150 core 2 3 in vec3 in_Position; 4 in vec3 in_Normal; 5 out vec4 pass_Position; 6 out vec4 pass_Color; 7 uniform mat4 modelMatrix; 8 uniform mat4 viewMatrix; 9 uniform mat4 projectionMatrix; 10 uniform vec3 lightPosition; 11 uniform vec3 lightColor; 12 uniform vec3 globalAmbient; 13 uniform float Kd; 14 15 void main(void) 16 { 17 gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0f); 18 vec3 worldPos = (viewMatrix * modelMatrix * vec4(in_Position, 1.0f)).xyz; 19 vec3 N = (transpose(inverse(viewMatrix * modelMatrix)) * vec4(in_Normal, 1.0f)).xyz; 20 N = normalize(N); 21 // light's direction 22 vec3 L = (viewMatrix * vec4(lightPosition, 1.0f)).xyz - worldPos;// point light 23 L = normalize(L); 24 // diffuse color from directional light 25 vec3 diffuseColor = Kd * lightColor * max(dot(N, L), 0); 26 // ambient color 27 vec3 ambientColor = Kd * globalAmbient; 28 pass_Color.xyz = diffuseColor + ambientColor; 29 pass_Color.w = 1; 30 }
首先, gl_Position 的计算是不用解释了。
要计算漫反射下的点光源照射物体的效果,需要知道物体的顶点的法线(normal)、顶点位置到光源的方向(light direction),两者夹角越小,那么光照越强。 max(dot(N, L), 0) 就是在计算光照强度。
为了避免全黑,我们加个环境光(ambient light)。环境光,就是那些综合起来的光照因素,不好准确计算,就简单地用一个 vec3 globalAmbient; 来描述了。
重要知识点
MVP的含义(projection * view * model)
那三个矩阵变换里,每个都有各自的意义。其中,modelMatrix是在物体坐标系旋转缩放平移模型。就好比父母在家里打扮自己的宝宝。ViewMatrix是把物体放到世界坐标系下,让摄像机为原点(0,0,0),来观察模型。就好比把各家各户的宝宝放到幼儿园拍合影。ProjectionMatrix就是宝宝们的合影照片。
所以, (viewMatrix * modelMatrix * vec4(in_Position, 1.0f)).xyz 就是物体的顶点在世界坐标系的位置。也就是在场景中的位置。(不是在3dmax里的位置)
法线(normal)
但是,不能以此类推世界坐标系里的法线(normal)。法线从3dmax中的值变换到场景中后,它的值应该是 (transpose(inverse(viewMatrix * modelMatrix)) * vec4(in_Normal, 1.0f)).xyz (就是要求逆然后转置),而不是 (viewMatrix * modelMatrix * vec4(in_Normal, 1.0f)).xyz 。具体原因可参考这里(http://blog.csdn.net/racehorse/article/details/6664775)。或者
Normals are funny. They're vec3's, since you don't want perspective on normals. And they don't actually scale quite right--a 45 degree surface with a 45 degree normal, scaled by glScalef(1,0.1,1), drops the surface down to near 0 degrees, but actually tilts the normal *up*, in the opposite direction from the surface, to near 90 degrees.Mathematically, if between two points a and b on the surface, dot(n,b-a)==0, then after applying a matrix M to the points, you want the normal to still be perpendicular. The question is, what matrix N do you have to apply to the normal to make this happen? In other words, find N such thatdot( N * n , M * a - M * b) == 0We can solve this by noting that dot product can be expresed as matrix multiplication--dot(x,y) = transpose(x) * y, where we treat an ordinary column-vector as a little matrix, and flip it horizontally. Sotranspose(N * n) * (M*a - M*b) == 0 (as above, but write using transpose and matrix multiplication)transpose(N * n) * M * (a-b) == 0 (collect both copies of M)transpose(n) * transpose(N) * M * (a-b) == 0 (transpose-of-product is product-of-transposes in opposite order)OK. This is really similar to our assumption that the original normal was perpendicular to the surface--that dot(n,b-a) == transpose(n) * (a-b) == 0. In fact, the only difference is the new matrices wedged in the middle. If we pick N to make the term in the middle the identity, then our new normal will be perpendicular to the surface too:transpose(N) * M == I (the identity matrix) This is the definition for matrix inverses, so the "normal matrix" N = transpose(inverse(M)).If you look up the GLSL definition for "gl_NormalMatrix", it's defined as "the transpose of the inverse of the gl_ModelViewMatrix". Now you know why!
normal
光源的位置
由于模型受到viewMatrix的影响,所以摄像机的改变也会改变模型的顶点位置,而点光源的位置如果不变,就会出现不同的光照结果。这不合实际。所以点光源也要按viewMatrix做变换。
Fragment shader
极其简单。
1 #version 150 core 2 3 in vec4 pass_Color; 4 out vec4 out_Color; 5 6 void main(void) 7 { 8 out_Color = pass_Color; 9 }
平行光源(directional light)
评选光源与之类似。至于为何在计算平行光源的方向时要用 (transpose(inverse(viewMatrix * modelMatrix)) * vec4(in_Normal, 1.0f)).xyz 这种复杂的步骤,原理在上文的法线(normal)中科院找到。(提示:都是为了让摄像机对模型和对光源方向产生同样的变换,从而使得摄像机的移动不会改变光照效果。)
1 #version 150 core 2 3 in vec3 in_Position; 4 in vec3 in_Normal; 5 out vec4 pass_Position; 6 out vec4 pass_Color; 7 uniform mat4 modelMatrix; 8 uniform mat4 viewMatrix; 9 uniform mat4 projectionMatrix; 10 uniform vec3 lightPosition; 11 uniform vec3 lightColor; 12 uniform vec3 globalAmbient; 13 uniform float Kd; 14 15 void main(void) 16 { 17 gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0f); 18 vec3 worldPos = (viewMatrix * modelMatrix * vec4(in_Position, 1.0f)).xyz; 19 vec3 N = (transpose(inverse(viewMatrix * modelMatrix)) * vec4(in_Normal, 1.0f)).xyz; 20 N = normalize(N); 21 // light's direction 22 vec3 L = (transpose(inverse(viewMatrix)) * vec4(lightPosition, 1.0f)).xyz;// directional light 23 L = normalize(L); 24 // diffuse color from directional light 25 vec3 diffuseColor = Kd * lightColor * max(dot(N, L), 0); 26 // ambient color 27 vec3 ambientColor = Kd * globalAmbient; 28 pass_Color.xyz = diffuseColor + ambientColor; 29 pass_Color.w = 1; 30 }
Fragment shader与上文的相同。
试验
在一个场景中,一个点光源照射到物体上。如果物体旋转,那么光照效果会改变。如果光源位置改变,那么光照效果会改变。但是,如果摄像机改变位置,却不会改变漫反射的效果。(漫反射,反射到各个角度的光效是相同的,所以与摄像机位置无关。)
您可以下载此示例(https://github.com/bitzhuwei/CSharpGL)试验,鼠标左键旋转模型,光效会改变。鼠标右键旋转摄像机,光效是不变的。
总结
今后将继续整合一些镜面反射等类型的光照效果,为更多更强的shader效果做准备。
不得不说线性代数在计算机3D效果方面的应用彻底证明了它的强大。
学OpenGL有2年了,从NEHE到SharpGL,从《3D Math Primer for Graphics and Game Development》到《OpenGL Programming Guide》,算是对OpenGL有了初级的认识。最近我纠集整理了SharpGL,GLM,SharpFont等开源库,想做一个更好用的纯C#版OpenGL。欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)...相关推荐
- linear polarized light and plane polarized light(线性偏振光和平面偏振光)
linear polarized light and plane polarized light 引言 光学上的解读 引言 今天给大家介绍两个光学上的概念,linear polarized light ...
- android开发:Theme.Light.NoTitleBar和Theme.Light.NoTitleBar.Fullscreen的区别
参考: android的Theme.Light.NoTitleBar和Theme.Light.NoTitleBar.Fullscreen的区别
- Directional Light Map(Directional Irradiance)
Light Map是一个比较经典的技术,目前来说应该是一般游戏引擎中的标配,它很好的在一种拟全局光效果的质量和效率上做了中和.不过目前用的更多.质量更好的应该是Directional Light Ma ...
- 场景编辑器Atmospheric Directional Light UI
场景编辑器Atmospheric Directional Light UI 前言 项目工具需要做个一个可视化调整角度的UI. 这里想参考一下:Atmospheric Directional Light ...
- 灯光 (1)平行光(Directional Light)
1.平行光(Directional Light) 定义一个光线方向向量而不是位置向量来模拟一个定向光.着色器的计算基本保持不变,但这次我们将直接使用光的direction向量而不是通过position ...
- android:theme.holo.light.,Android: Theme.Holo.Light.NoActionBar vs Theme.Light.NoTitleBar
问题 In res/values-v11/styles.xml, I can't use Theme.Holo.Light.NoActionBar because it was added in AP ...
- BIT祝威博客汇总(Blog Index)
+BIT祝威+悄悄在此留下版了个权的信息说: 关于硬件(Hardware) <穿越计算机的迷雾>笔记 继电器是如何成为CPU的(1) 继电器是如何成为CPU的(2) 关于操作系统(Oper ...
- MATLAB中的光照处理
在用MATLAB绘制三维图形时,不仅可以画出带光照模式的曲面,还能在绘图时指定光线的来源. 1.带光照模式的三维曲面 surfl命令用来画一个带光照模式的三维曲面图,该命令显示一个带阴影的曲面,结合了 ...
- 8.3实例程序:平面阴影
在场景中被灯光照射的地方会产生阴影,这将使场景变的更真实.在这一部分我们将演示怎样实现平面阴影,即在平面上的阴影(如图8.5). 使用这种阴影只是一种权宜之计,虽然它增强了场景的真实效果,但是这并不是 ...
- [ZZ] D3D中的模板缓存(3)
http://www.cppblog.com/lovedday/archive/2008/03/25/45334.html http://www.cppblog.com/lovedday/ D3D中的 ...
最新文章
- 【WebAPI No.5】Core WebAPI中的自定义格式化
- python下载安装教程3.7.3-【最新】Python-3.7.0安装教程及下载链接
- 逐鹿东南亚,Lazada增长飞轮再加速
- Object.prototype.toString.call()检测
- jsp网上商城jsp课程设计
- Android phone xp 华为3x,3000mAh大电池 华为荣耀3X续航能力实测
- CSS 单行溢出文本显示省略号...的方法(兼容IE FF)(转)
- 简述台式计算机创建家庭组的步骤,如何创建和设置家庭组
- 2020天津理工大学计算机考研录取名单,天津理工大学2020年硕士研究生拟录取名单公示...
- svn服务器记录日志文件,svn服务器日志时间设置
- MAC编译OpenJDK8:error: invalid argument ‘-std=gnu++98‘ not allowed with ‘C‘
- cad连筋字体怎么安装_为什么我的CAD图纸会缺失字体?怎么解决?
- 【数理统计】一题了解拟合优度检验
- PROFINET协议
- IERS EOP 文件的解读
- 假币问题 (n枚硬币+未知轻重+DFS)
- The following packages have unmet dependencies
- mdx格式mysql_Saiku_学习_02_Schema Workbench 开发mdx和模式文件
- 梅科尔工作室-孙溢博-鸿蒙笔记1
- ACL的原理与基本ACL的配置
热门文章
- 微信小程序:uu传书
- JavaSE_day12:集合,泛型,增强for循环特性
- 项目Beta冲刺(4/7)(追光的人)(2019.5.26)
- 花式感染 Windows PE 病毒的一百种方法,你中过招吗?
- 自下而上和自上而下的注意力:不同的过程和重叠的神经系统 2014sci
- 监护仪系统都是Linux吗,基于Linux和MCU的心电监护仪设计
- 解决在stata横向合并出现的not uniquely identify observations in the using/master data
- 三角(Triangle)
- 韩式时尚大片《九月风》(出镜:马晓辉/徐嘉依)
- 需求和设计阶段使用的IPO图