GLSL聚光灯投影量

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)

屏幕截图:

在此处输入图片说明

Rab*_*d76 5

为了使聚光灯的视锥原始可见,必须将视线与视锥相交。
以下算法在透视视图中起作用,并且计算在视图(眼睛)空间中进行。该算法不关心场景的几何形状,并且不执行任何深度测试或阴影测试,它只是光锥的叠加可视化。

透视图中的视线可以由点和方向确定。由于计算是在视(眼)空间中进行的,因此该点即为的视点(视锥的原点)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 );


Copyright Info

© Copyright 2013-2021 admin@qa.1r1g.com

如未特别说明,本网站的内容使用如下协议:
Creative Commons Atution-NonCommercial-ShareAlike 4.0 International license
.

用以下方式浏览
回到顶部