您的位置:首页 > 编程语言 > Qt开发

现代OpenGL+Qt学习笔记之十二:模拟聚光灯

2017-10-12 21:48 567 查看

现代OpenGL+Qt学习笔记之十二:模拟聚光灯

主要内容

  本文将实现一种特殊的光源,该光源的位置定义在一个圆锥的顶点上,光的照射范围仅在圆锥范围内,且光强在该圆锥的中心轴上时最强的,而随着空间点到该中心轴的距离的增加,光强也随之减少。这就是现实中的聚光灯效果,本文将通过GLSL介绍如何模拟这种现实中的聚光灯。效果如下图所示:



聚光灯模拟

  通过上面的介绍我们知道,聚光灯光源是由一个圆锥确定其位置、方向、照射范围等属性的,那么我们如何定义该圆锥呢?



  如图,要确定该圆锥,需要一个光源方向d,光源位置p以及一个截光角(cutoff angle)c,有了这些量,该圆锥也就确定了,要完全确定光源,还需要光源在中心轴处的最强光强,以及光强向圆锥边界的衰减方式。

  本文将采用逐片元渲染的方式实现聚光灯的模拟,即主要聚光灯效果的实现是在片元着色器中实现的。在实现中,我们主要要考虑两种情况,一是当要渲染的点在圆锥之外时,我们将只计算和保留该点的环境光分量,而当要渲染的点是在圆锥之内时,我们就要分别计算3种光的分量,这里和之前的不同点是需要先计算该点的光强。本文的代码相比较之前的代码修改比较大,首先删除了3个事件处理函数,取而代之的用一个计时器触发渲染的动态效果。具体实现可见源码。

  下面重点介绍其中的着色器的实现,其中片元着色器的内容如下,实现了主要的光照计算功能:

#version 430

in vec3 Position;
in vec3 Normal;

struct SpotLightInfo {
vec4 position;   // Position in eye coords
vec3 intensity;
vec3 direction;  // Direction of the spotlight in eye coords.
float exponent;  // Angular attenuation exponent
float cutoff;    // Cutoff angle (between 0 and 90)
};
uniform SpotLightInfo Spot;

uniform vec3 Kd;            // Diffuse reflectivity
uniform vec3 Ka;            // Ambient reflectivity
uniform vec3 Ks;            // Specular reflectivity
uniform float Shininess;    // Specular shininess factor

layout( location = 0 ) out vec4 FragColor;

vec3 adsWithSpotlight( )
{
vec3 n = normalize(Normal);
vec3 s = normalize( vec3( Spot.position) - Position );
vec3 spotDir = normalize( Spot.direction);
float angle = acos( dot(-s, spotDir) );
float cutoff = radians( clamp( Spot.cutoff, 0.0, 90.0 ) );
vec3 ambient = Spot.intensity * Ka;

if( angle < cutoff ) {
float spotFactor = pow( dot(-s, spotDir), Spot.exponent );
vec3 v = normalize(vec3(-Position));
vec3 h = normalize( v + s );

return
ambient +
spotFactor * Spot.intensity * (
Kd * max( dot(s, n), 0.0 ) +
Ks * pow( max( dot(h,n), 0.0 ), Shininess )
);
} else {
return ambient;
}
}

void main() {
FragColor = vec4(adsWithSpotlight(), 1.0);
}


  顶点着色器的内容修改如下:

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

out vec3 Position;
out vec3 Normal;

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
Normal = normalize( No
c1be
rmalMatrix * VertexNormal);
Position = vec3( ModelViewMatrix * vec4(VertexPosition,1.0) );

gl_Position = MVP * vec4(VertexPosition,1.0);
}


  顶点着色器只进行了简单法向和位置变换并输出到下一个阶段。在片元着色器中定义的聚光灯光源共有5个属性,其中4个前面已经有直接的介绍,分别是位置position、截光角cutoff、中心轴方向direction和光强intensity,还有一个是exponent,是一个光强随着角度的衰减指数,当空间点到光源的连线向量(-s)与中心轴方向的夹角增大时,光想会以指数衰减,即衰减的因子定义为:

factor=(−s⋅spotDir)exponent

见片元着色器32行,其中spotDir是中心轴方向的单位化向量。31行的判断是判断当前计算的点是否在光源照射范围内,如果不在,就只会计算该点的环境光。

 &emp;在主程序端对所有的uniform变量进行赋值,并给定几个要渲染的对象,本示例中给了3个对象,分别是一个圆环体、一个茶壶、和一个平面(作为地面),其中圆环体和茶壶前面其实都接触过了,平面的添加方式基本相同。

  本示例还利用QTimer对象实现了聚光灯方向的动态变换效果。最终的实现结果如下(具体实现细见源码):



源码地址:http://download.csdn.net/download/chaojiwudixiaofeixia/10020811
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息