繁体   English   中英

在OpenGL中创建矩形光源?

[英]Creating a rectangular light source in OpenGL?

我正在尝试为一个应用程序在OpenGL中创建一个矩形,锐利的光源。 我的想法是创建一个聚光灯,并以某种方式将阴影的形状蒙版为矩形,当然,蒙版必须通过相机看不见。 当我尝试实现此想法时,事实证明OpenGL会跳过摄像机外部的渲染对象,尽管摄像机外部的光源仍然有效。 这使我无法产生想要的效果,我想知道以前是否有人遇到过类似的问题。

为了使我的问题更具体,请考虑以下情况:

0,0,5的聚光灯

目标对象位于0,0,0

在0,0,3处遮罩对象(平行于x轴的简单四边形)。

当相机位于0,0,4时,光线穿过蒙版对象并在目标对象上留下矩形形状(这是我想要的),但是我也可以看到蒙版对象!(同时我需要将蒙版对象设置为无形)

当我将相机移近目标物体时,说0,0,2。 遮罩对象在相机后面,因此是不可见的。 但是,由于它是不可见的,OpenGL停止了对其进行渲染,因此mask对象对目标对象没有任何影响,并且阴影仍然是圆形的!

我的猜测是从聚光灯开始,但分开进行角度计算:*将L向量投影在YZ平面上以计算X轴上的角度*将L向量投影在XZ平面上以计算Y上的角度轴

一个非常幼稚的实现可能是(GLSL):

varying vec3 v_V; // World-space position
varying vec3 v_N; // World-space normal

uniform float time; // global time in seconds since shaderprogram link
uniform vec2 uSpotSize; // Spot size, on X and Y axes

vec3 lp = vec3(0.0, 0.0, 7.0 + cos(time) * 5.0); // Light world-space position
vec3 lz = vec3(0.0, 0.0, -1.0); // Light direction (Z vector)

// Light radius (for attenuation calculation)
float lr = 3.0;

void main()
{
    // Calculate L, the vector from model surface to light
    vec3 L = lp - v_V;

    // Project L on the YZ / XZ plane
    vec3 LX = normalize(vec3(L.x, 0.0, L.z));
    vec3 LY = normalize(vec3(0.0, L.y, L.z));

    // Calculate the angle on X and Y axis using projected vectors just above
    float ax = dot(LX, -lz);
    float ay = dot(LY, -lz);

    // Light attenuation
    float d = distance(lp, v_V);
    float attenuation = 1.0 / (1.0 + (2.0/lr)*d + (1.0/(lr*lr))*d*d);

    float shaded = max(0.0, dot(v_N, L)) * attenuation;

    if(ax > cos(uSpotSize.x) && ay > cos(uSpotSize.y))
        gl_FragColor = vec4(shaded); // Inside the light influence zone, light it up !
    else
        gl_FragColor = vec4(0.1); // Outside the light influence zone.
}

再次,这是非常幼稚的。 例如,X / Y投影是在世界空间中完成的。 如果要旋转光源矩形,则可能必须引入指向光源右侧的向量。 因此,您将能够在光源的坐标系中获取片段坐标,并以此决定是否着色片段。

一种解决方案可能是调整用于投影纹理查找的计算以模拟矩形光源。 您没有指定要使用的OpenGL版本,但投射式纹理查找甚至可以通过固定功能管线实现-尽管可以说它们在着色器中更容易实现。

在此处输入图片说明

当然,这将不会模拟矩形区域光源,而只能模拟约束到矩形区域的点光源。

使用这种方法,您必须为光源指定视图和投影矩阵。 视点矩阵基本上是由“看”到灯光位置及其方向生成的; 投影矩阵使用您所需的水平和垂直“视场”对透视投影进行编码。

如果只需要一个矩形区域,则甚至不需要纹理。 一个简单的顶点/片段着色器对可能看起来像这样:(顶点着色器基本上将位置转换为光源的剪贴空间,片段着色器执行裁剪并计算片段(如果片段位于视锥内部)

#version 330 core

layout ( location = 0 ) in vec3 vertexPosition;
layout ( location = 1 ) in vec3 vertexNormal;
layout ( location = 3 ) in vec3 vertexDiffuse;

uniform mat4 modelTf;
uniform mat3 normalTf;
uniform mat4 viewTf;                    // view matrix for render camera
uniform mat4 projectiveTf;              // projection matrix for render camera
uniform mat4 viewTf_lightCam;           // view matrix of light source
uniform mat4 projectiveTf_lightCam;     // projective matrix of light source

uniform vec4 lightPosition_worldSpace;

out vec3 diffuseColor;
out vec3 normal_worldSpace;
out vec3 toLight_worldSpace;
out vec4 position_lightClipSpace;

void main()
{
    diffuseColor = vertexDiffuse;
    vec4 vertexPosition_worldSpace = modelTf * vec4( vertexPosition, 1.0 );
    normal_worldSpace = normalTf * vertexNormal;
    toLight_worldSpace = normalize( lightPosition_worldSpace - vertexPosition_worldSpace ).xyz;
    position_lightClipSpace = projectiveTf_lightCam * viewTf_lightCam * vertexPosition_worldSpace;
    gl_Position = projectiveTf * viewTf * vertexPosition_worldSpace;
}

#version 330 core

layout ( location=0 ) out vec4 fragColor;

in vec3 diffuseColor;
in vec3 normal_worldSpace;
in vec3 toLight_worldSpace;
in vec4 position_lightClipSpace;

uniform vec3 ambientLight;

void main()
{
    // clipping against the light frustum
    bool isInsideX = ( position_lightClipSpace.x <= position_lightClipSpace.w && position_lightClipSpace.x >= -position_lightClipSpace.w );
    bool isInsideY = ( position_lightClipSpace.y <= position_lightClipSpace.w && position_lightClipSpace.y >= -position_lightClipSpace.w );
    bool isInsideZ = ( position_lightClipSpace.z <= position_lightClipSpace.w && position_lightClipSpace.z >= -position_lightClipSpace.w );
    bool isInside = isInsideX && isInsideY && isInsideZ;

    vec3 N = normalize( normal_worldSpace );
    vec3 L = normalize( toLight_worldSpace );
    vec3 lightColor = isInside ? max( dot( N, L ), 0.0 ) * vec3( 0.99, 0.66, 0.33 ) : vec3( 0.0 );

    fragColor = vec4( clamp( ( ambientLight + lightColor ) * diffuseColor, vec3( 0.0 ), vec3( 1.0 ) ), 1.0 );
}

关于这一点,有很多好的论文,Brian Karis在2013年(关于UE4)写到了这里:

https://de45xmedrsdbp.cloudfront.net/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf

最近,Michal Drobot撰写了一篇有关GPU Pro 5中区域照明的文章。

如果您使用的是金属化工作流程,您还可以提高粗糙度,以近似于区域照明,这是Tri-Ace引入的一种技术:

http://www.fxguide.com/featured/game-environments-parta-remember-me-rendering/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM