L7V1:OPENGL 着色:学习动机
1.光照的重要性
1)能够真正显示出形状感知的外观;
2)准确的着色和光照对对传达物体的形状非常重要;
3)着色的方式也十分重要:平面着色(GL_FLAT)、平滑着色(GL_SMOOTH)。
2.颜色
1)RGB模式:分为Red、Green、Blue三个通道
2)RGBA模式:OpenGL中常用此模式,A代表着透明度,每个通道占8bit,总共4字节。
L7V2:OPENGL 着色:Gouraud and Phong Shading
1.Gouraud Shading--标准的平滑着色
1)方法:采用线性插值的方法进行平滑着色;
2)变量说明:I1、I2、I3分别为三个顶点的颜色值,Ip为所要求的颜色值,Ia、Ib为横向扫描线上的交点;
3)公式推导:
a.首先沿Y轴方向插值,得到Ia、Ib:
b.在扫描线上沿X轴方向插值,得到Ip:
4)极端情况:当以上公式分母等于0时,该公式无法进行插值;
解决方法:将模型细化,分割成足够多的三角形。
2.Phong Shading (≠ Phong Illumination)
1)思想:不是对颜色进行插值,而是对法向量进行插值;
2)整个光照计算是逐个像素进行的;
L7V3:OPENGL 着色:光照和着色
1.光源
1)属性:Position、Color;
2)衰减模型:
其中:atten代表光强,d代表距离,kc、kl、kq为常数;
3)常数设置(一般情况下,假设光源不存在能量衰竭):
a)点光源:(kc, kl, kq)=(0, 0, 1);
b)线光源:(kc, kl, kq)=(0, 1, 0);
c)面光源(方向光源):(kc, kl, kq)=(1, 0, 0),此处忽略光强衰减;
2.材质
1)计算法向量 -->进而计算漫反射、镜面反射的反射方向;需要计算出每一个顶点的法向量;
一些由GLUT提供的图形,GLUT已经计算好了法向量;
2)着色模式:
A)环境光(Ambient):即使没有光照射到表面,仍然有光在屋内来回反射,并且产生了漫反射或环境光的感觉;需要设置一个环境光强度,一般来说很小,使不存在黑色的像素;
B)漫反射(Diffuse):对应粗糙不光滑的表面,如墙壁、地板;光均匀地向各个方向反射;
C)镜面反射(Specular):对应于光滑的物体;
D)发射光(Emissive):对应于太阳光等光源,对应于直接看向光源发出的光,而不是反射方向的,在OpenGL中,想看到光源还必须创建光源的几何体;
3.Phong Illumination
1)简单依赖于视点的高光,不基于物理;
2)思想:a)在镜面反射上,选择视线和反射光线之间进行点积,更进一步,给cos项加上指数来控制表面的光泽度,高光到底有多亮;b)取光源、视线之间的角度的一半,求视线和法向的点积,这样会更接近;
3)Phong Formula
specular = Ks * lightcolor * (dot(V, R))shininess
【说明】
Ks:物体对于反射光线的衰减系数
N:表面法向量
H:光入射方向L和视点方向V的中间向量
Shininess:高光系数
Phong模型计算中的一个关键步骤就是反射向量R的计算:
上图中的位于表面“下面”的向量 ‘I’ 是原始 ‘I’ 向量的拷贝,并且二者是一样的,现在我们的目标计算出向量'R'。根据向量相加原则,向量'R'等于'I'+'V','I'是已知的,所以我们需要做的就是找出向量'V'。注意法向量 ‘N’ 的负方向就是 ‘-N’,我们可以在 ‘I’ 和 ‘-N’ 之间使用一个点乘运算就能得到 ‘I’ 在 ‘-N’ 上面的投影的模。这个模正好是 ‘V’ 的模的一半,由于 ‘V’ 与 ‘N’ 有相同的方向,我们可以将这个模乘上 ‘N’ (其模为 1 )再乘上 2 即可得到 ‘V’。总结一下就是下面的公式:
R = I + V
V = 2 * N * ( -N * I )
得出:R = I + 2 * N * ( -N * I ) = I - 2 * N * ( N * I )
4)Blinn-Phong Formula
Phong模型中计算反射光线的向量是一件相对比较耗时的任务,因此Blinn-Phong对这一点进行了改进。
specular = Ks * lightcolor * (dot(V, H))shininess
【说明】
Ks:物体对于反射光线的衰减系数
N:表面法向量
H:光入射方向L和视点方向V的中间向量
Shininess:高光系数
可见,通过该式计算镜面反射光是符合基本规律的,当视点方向和反射光线方向一致时,计算得到的H与N平行,dot(N,H)取得最大;当视点方向V偏离反射方向时,H也偏离N。同时H的计算比起反射向量R的计算简单的多,R向量的计算需要若干次的向量乘法与加法,而H的计算仅仅需要一次加法。
L7V4:OPENGL 着色:片元着色器
1.片元着色器的设置
1 # version 120
2
3 varying vec4 color ;
4 varying vec3 mynormal ;
5 varying vec4 myvertex ;
6
7 uniform sampler2D tex ;
8 uniform int istex ;
9 uniform int islight ; // are we lighting.
【说明】
3-5行:由顶点着色器简单传入颜色、法向、顶点;7-8行:sampler2D tex用于纹理,istex表示是否是纹理,islight表示是否是光源,uniform表示在片元着色器中一直不变;
2.着色器变量
1 // Assume light 0 is directional, light 1 is a point light.
2 // The actual light values are passed from the main OpenGL program.
3
4 uniform vec3 light0dirn ;
5 uniform vec4 light0color ;
6 uniform vec4 light1posn ;
7 uniform vec4 light1color ;
8
9 // Now, set the material parameters. These could be varying and/or
10 // bound to a buffer. But for now, I'll just make them uniform.
11
12 uniform vec4 ambient ;
13 uniform vec4 diffuse ;
14 uniform vec4 specular ;
15 uniform float shininess ;
【说明】
6行:vec4 light1posn:这是齐次坐标下一个位置处的光源;
12-15行:设置材质参数:环境光系数、漫反射系数、镜面反射系数、光泽度;
3.计算光照
1 vec4 ComputeLight (const in vec3 direction, const in vec4 lightcolor,
2 const in vec3 normal, const in vec3 halfvec, const in vec4 mydiffuse,
3 const in vec4 myspecular, const in float myshininess) {
4
5 float nDotL = dot(normal, direction) ;
6 vec4 lambert = mydiffuse * lightcolor * max (nDotL, 0.0) ;
7
8 float nDotH = dot(normal, halfvec) ;
9 vec4 phong = myspecular * lightcolor * pow (max(nDotH, 0.0), myshininess) ;
10
11 vec4 retval = lambert + phong ;
12 return retval ;
13 }
【说明】此处算法参考Phong光照模型
4.主要变换代码
1 // They eye is always at (0,0,0) looking down -z axis
2 // Also compute current fragment position and direction to eye
3
4 const vec3 eyepos = vec3(0,0,0) ;
5 vec4 _mypos = gl_ModelViewMatrix * myvertex ;
6 vec3 mypos = _mypos.xyz / _mypos.w ; // Dehomogenize current location
7 vec3 eyedirn = normalize(eyepos - mypos) ;
8
9 // Compute normal, needed for shading.
10 // Simpler is vec3 normal = normalize(gl_NormalMatrix * mynormal) ;
11 vec3 _normal = (gl_ModelViewMatrixInverseTranspose*vec4(mynormal,0.0)).xyz ;
12 vec3 normal = normalize(_normal) ;
13
14 // Light 0, directional
15 vec3 direction0 = normalize (light0dirn) ;
16 vec3 half0 = normalize (direction0 + eyedirn) ;
17 vec4 col0 = ComputeLight(direction0, light0color, normal, half0, diffuse, specular, shininess) ;
18
19 // Light 1, point
20 vec3 position = light1posn.xyz / light1posn.w ;
21 vec3 direction1 = normalize (position - mypos) ; // no attenuation
22 vec3 half1 = normalize (direction1 + eyedirn) ;
23 vec4 col1 = ComputeLight(direction1, light1color, normal, half1, diffuse, specular, shininess) ;
24
25 gl_FragColor = ambient + col0 + col1 ;
【说明】11行:思维矩阵转置后,我们只取xyz分量部分,去掉齐次坐标部分;