Mas*_*ari 4 opengl lighting glsl light qt3d
在我的开源项目中,我使用Qt3D设置了延迟渲染管道。到目前为止,一切都很好,但是现在我想通过增加聚光灯的投影体积来向前迈进。(例如,好像在现场冒烟)是这样的:
我正在使用的片段着色器在问题的结尾。我已经读过,对于每个片段,我都应该从光线位置进行光线行进,并找到与圆锥体的交点,但是我不知道如何将其转换为GLSL。我可以轻松地添加来自GBuffer的深度图(从摄影机的角度来看)的制服,但是我不知道这是否有帮助。
由于我的GLSL知识非常有限,请以实际代码答复,而不是冗长的数学解释,我无法理解/将其翻译为代码。请耐心等待我。
uniform sampler2D color;
uniform sampler2D position;
uniform sampler2D normal;
uniform vec2 winSize;
out vec4 fragColor;
const int MAX_LIGHTS = 102;
const int TYPE_POINT = 0;
const int TYPE_DIRECTIONAL = 1;
const int TYPE_SPOT = 2;
struct Light {
int type;
vec3 position;
vec3 color;
float intensity;
vec3 direction;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
float cutOffAngle;
};
uniform Light lightsArray[MAX_LIGHTS];
uniform int lightsNumber;
void main()
{
vec2 texCoord = gl_FragCoord.xy / winSize;
vec4 col = texture(color, texCoord);
vec3 pos = texture(position, texCoord).xyz;
vec3 norm = texture(normal, texCoord).xyz;
vec3 lightColor = vec3(0.0);
vec3 s;
float att;
for (int i = 0; i < lightsNumber; ++i) {
att = 1.0;
if ( lightsArray[i].type != TYPE_DIRECTIONAL ) {
s = lightsArray[i].position - pos;
if (lightsArray[i].constantAttenuation != 0.0
|| lightsArray[i].linearAttenuation != 0.0
|| lightsArray[i].quadraticAttenuation != 0.0) {
float dist = length(s);
att = 1.0 / (lightsArray[i].constantAttenuation + lightsArray[i].linearAttenuation * dist + lightsArray[i].quadraticAttenuation * dist * dist);
}
s = normalize( s );
if ( lightsArray[i].type == TYPE_SPOT ) {
if ( degrees(acos(dot(-s, normalize(lightsArray[i].direction))) ) > lightsArray[i].cutOffAngle)
att = 0.0;
}
} else {
s = normalize(-lightsArray[i].direction);
}
float diffuse = max( dot( s, norm ), 0.0 );
lightColor += att * lightsArray[i].intensity * diffuse * lightsArray[i].color;
}
fragColor = vec4(col.rgb * lightColor, col.a);
}
Run Code Online (Sandbox Code Playgroud)
[编辑-已解决]感谢Rabbid76出色的回答和宝贵的支持
这是修改后的代码以查看圆锥投影:
#version 140
uniform sampler2D color;
uniform sampler2D position;
uniform sampler2D normal;
uniform vec2 winSize;
out vec4 fragColor;
const int MAX_LIGHTS = 102;
const int TYPE_POINT = 0;
const int TYPE_DIRECTIONAL = 1;
const int TYPE_SPOT = 2;
struct Light {
int type;
vec3 position;
vec3 color;
float intensity;
vec3 direction;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
float cutOffAngle;
};
uniform Light lightsArray[MAX_LIGHTS];
uniform int lightsNumber;
uniform mat4 inverseViewMatrix; // defined by camera position, camera target and up vector
void main()
{
vec2 texCoord = gl_FragCoord.xy / winSize;
vec4 col = texture(color, texCoord);
vec3 pos = texture(position, texCoord).xyz;
vec3 norm = texture(normal, texCoord).xyz;
vec3 lightColor = vec3(0.0);
vec3 s;
// calculate unprojected fragment position on near plane and line of sight relative to view
float nearZ = -1.0;
vec3 nearPos = vec3( (texCoord.x - 0.5) * winSize.x / winSize.y, texCoord.y - 0.5, nearZ ); // 1.0 is camera near
vec3 los = normalize( nearPos );
// ray definition
vec3 O = vec3( inverseViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ) ); // translation part of the camera matrix, which is equal to the camera position
vec3 D = (length(pos) > 0.0) ? normalize(pos - O) : (mat3(inverseViewMatrix) * los);
for (int i = 0; i < lightsNumber; ++i)
{
float att = 1.0;
if ( lightsArray[i].type == TYPE_DIRECTIONAL )
{
s = normalize( -lightsArray[i].direction );
}
else
{
s = lightsArray[i].position - pos;
if (lightsArray[i].type != TYPE_SPOT
&& (lightsArray[i].constantAttenuation != 0.0
|| lightsArray[i].linearAttenuation != 0.0
|| lightsArray[i].quadraticAttenuation != 0.0))
{
float dist = length(s);
att = 1.0 / (lightsArray[i].constantAttenuation + lightsArray[i].linearAttenuation * dist + lightsArray[i].quadraticAttenuation * dist * dist);
}
s = normalize( s );
if ( lightsArray[i].type == TYPE_SPOT )
{
// cone definition
vec3 C = lightsArray[i].position;
vec3 V = normalize(lightsArray[i].direction);
float cosTh = cos( radians(lightsArray[i].cutOffAngle) );
// ray - cone intersection
vec3 CO = O - C;
float DdotV = dot( D, V );
float COdotV = dot( CO, V );
float a = DdotV * DdotV - cosTh * cosTh;
float b = 2.0 * (DdotV * COdotV - dot( D, CO ) * cosTh * cosTh);
float c = COdotV * COdotV - dot( CO, CO ) * cosTh * cosTh;
float det = b * b - 4.0 * a * c;
// find intersection
float isIsect = 0.0;
vec3 isectP = vec3(0.0);
if ( det >= 0.0 )
{
vec3 P1 = O + (-b - sqrt(det)) / (2.0 * a) * D;
vec3 P2 = O + (-b + sqrt(det)) / (2.0 * a) * D;
float isect1 = step( 0.0, dot(normalize(P1 - C), V) );
float isect2 = step( 0.0, dot(normalize(P2 - C), V) );
if ( isect1 < 0.5 )
{
P1 = P2;
isect1 = isect2;
}
if ( isect2 < 0.5 )
{
P2 = P1;
isect2 = isect1;
}
isectP = (length(P1 - O) < length(P2 - O)) ? P1 : P2;
isIsect = mix( isect2, 1.0, isect1 );
if ( length(pos) != 0.0 && length(isectP - O) > length(pos - O))
isIsect = 0.0;
}
float dist = length( isectP - C.xyz );
float limit = degrees(acos(dot(-s, normalize(lightsArray[i].direction))) );
if (isIsect > 0 || limit <= lightsArray[i].cutOffAngle)
{
att = 1.0 / dot( vec3( 1.0, dist, dist * dist ),
vec3(lightsArray[i].constantAttenuation,
lightsArray[i].linearAttenuation,
lightsArray[i].quadraticAttenuation) );
}
else
att = 0.0;
}
}
float diffuse = max( dot( s, norm ), 0.0 );
lightColor += att * lightsArray[i].intensity * diffuse * lightsArray[i].color;
}
fragColor = vec4(col.rgb * lightColor, col.a);
}
Run Code Online (Sandbox Code Playgroud)
传递给着色器的制服是:
qml: lightsArray[0].type = 0
qml: lightsArray[0].position = QVector3D(0, 10, 0)
qml: lightsArray[0].color = #ffffff
qml: lightsArray[0].intensity = 0.8
qml: lightsArray[0].constantAttenuation = 1
qml: lightsArray[0].linearAttenuation = 0
qml: lightsArray[0].quadraticAttenuation = 0
qml: lightsArray[1].type = 2
qml: lightsArray[1].position = QVector3D(0, 3, 0)
qml: lightsArray[1].color = #008000
qml: lightsArray[1].intensity = 0.5
qml: lightsArray[1].constantAttenuation = 2
qml: lightsArray[1].linearAttenuation = 0
qml: lightsArray[1].quadraticAttenuation = 0
qml: lightsArray[1].direction = QVector3D(-0.573576, -0.819152, 0)
qml: lightsArray[1].cutOffAngle = 15
qml: lightsNumber = 2
Run Code Online (Sandbox Code Playgroud)
屏幕截图:
为了使聚光灯的视锥原始可见,必须将视线与视锥相交。
以下算法在透视视图中起作用,并且计算在视图(眼睛)空间中进行。该算法不关心场景的几何形状,并且不执行任何深度测试或阴影测试,它只是光锥的叠加可视化。
透视图中的视线可以由点和方向确定。由于计算是在视(眼)空间中进行的,因此该点即为的视点(视锥的原点)vec3(0.0)。
通过视线和相机视锥的近平面的交点可以轻松确定方向。这可以容易地计算出,如果投影XY坐标的片段的规格化设备坐标(左下点为(-1,-1)和右上点是(1,1是已知的)看便知到这个问题)。
float aspect = .....; // ratio of the view port (widht/length)
float fov = .....; // filed of view angle in radians (angle of the camera frustum on the Y-axis)
vec2 ndcPos = .....; // fragment position in NDC space from (-1,-1) to (1,1)
vec3 tanFov = tan( fov * 0.5 );
vec3 los = normalize( vec3( ndcPos.x * aspect * tanFov, ndcPos.y * tanFov, -1.0 ) );
Run Code Online (Sandbox Code Playgroud)
视锥由光源的原点,光源指向的方向以及视锥的整个角度定义。位置和方向必须在视图空间中向上。角度必须以弧度为单位。
vec3 vLightPos = .....; // position of the light source in view space
vec3 vLightDir = .....; // direction of the light in view space
float coneAngle = .....; // full angle of the light cone in radians
Run Code Online (Sandbox Code Playgroud)
如何计算射线与圆锥的交点可以在Stackoverflow问题的答案中找到,矢量与圆锥的交点以及在以下论文中:射线与圆锥的交点。
以下代码计算如上定义的射线和圆锥的交点。结果点存储在中isectP。如果存在交点,isIsect则类型变量float设置为1.0,否则设置为0.0。
// ray definition
vec3 O = vec3(0.0);
vec3 D = los;
// cone definition
vec3 C = vLightPos;
vec3 V = vLightDir;
float cosTh = cos( coneAngle * 0.5 );
// ray - cone intersection
vec3 CO = O - C;
float DdotV = dot( D, V );
float COdotV = dot( CO, V );
float a = DdotV*DdotV - cosTh*cosTh;
float b = 2.0 * (DdotV*COdotV - dot( D, CO )*cosTh*cosTh);
float c = COdotV*COdotV - dot( CO, CO )*cosTh*cosTh;
float det = b*b - 4.0*a*c;
// find intersection
float isIsect = 0.0;
vec3 isectP = vec3(0.0);
if ( det >= 0.0 )
{
vec3 P1 = O + (-b-sqrt(det))/(2.0*a) * D;
vec3 P2 = O + (-b+sqrt(det))/(2.0*a) * D;
float isect1 = step( 0.0, dot(normalize(P1-C), V) );
float isect2 = step( 0.0, dot(normalize(P2-C), V) );
P1 = mix( P2, P1, isect1 );
isectP = P2.z < 0.0 && P2.z > P1.z ? P2 : P1;
isIsect = mix( isect2, 1.0, isect1 ) * step( isectP.z, 0.0 );
}
Run Code Online (Sandbox Code Playgroud)
有关完整的GLSL代码,请参见以下WebGL示例:
float aspect = .....; // ratio of the view port (widht/length)
float fov = .....; // filed of view angle in radians (angle of the camera frustum on the Y-axis)
vec2 ndcPos = .....; // fragment position in NDC space from (-1,-1) to (1,1)
vec3 tanFov = tan( fov * 0.5 );
vec3 los = normalize( vec3( ndcPos.x * aspect * tanFov, ndcPos.y * tanFov, -1.0 ) );
Run Code Online (Sandbox Code Playgroud)
vec3 vLightPos = .....; // position of the light source in view space
vec3 vLightDir = .....; // direction of the light in view space
float coneAngle = .....; // full angle of the light cone in radians
Run Code Online (Sandbox Code Playgroud)
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec3 inPos;
attribute vec3 inNV;
attribute vec3 inCol;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
varying vec4 clip_space_pos;
uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
void main()
{
vec3 modelNV = mat3( u_modelMat44 ) * normalize( inNV );
vertNV = mat3( u_viewMat44 ) * modelNV;
vertCol = inCol;
vec4 modelPos = u_modelMat44 * vec4( inPos, 1.0 );
vec4 viewPos = u_viewMat44 * modelPos;
vertPos = viewPos.xyz / viewPos.w;
gl_Position = u_projectionMat44 * viewPos;
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec3 vertPos;
varying vec3 vertNV;
varying vec3 vertCol;
struct Light {
vec3 position;
vec3 direction;
float ambient;
float diffuse;
float specular;
float shininess;
vec3 attenuation;
float cutOffAngle;
};
uniform Light u_light;
void main()
{
vec3 color = vertCol;
vec3 lightCol = u_light.ambient * color;
vec3 normalV = normalize( vertNV );
vec3 lightV = normalize( u_light.position - vertPos );
float lightD = length( u_light.position - vertPos );
float cosL = dot( normalize( u_light.direction ), -lightV );
float inCone = step( cos( u_light.cutOffAngle * 0.5 ), cosL );
float att = 1.0 / dot( vec3( 1.0, lightD, lightD*lightD ), u_light.attenuation );
float NdotL = max( 0.0, dot( normalV, lightV ) );
lightCol += NdotL * u_light.diffuse * color * inCone * att;
vec3 eyeV = normalize( -vertPos );
vec3 halfV = normalize( eyeV + lightV );
float NdotH = max( 0.0, dot( normalV, halfV ) );
float kSpecular = ( u_light.shininess + 2.0 ) * pow( NdotH, u_light.shininess ) / ( 2.0 * 3.14159265 );
lightCol += kSpecular * u_light.specular * color * inCone * att;
gl_FragColor = vec4( lightCol.rgb, 1.0 );