在OpenGL中创建矩形光源?

时间:2014-04-09 04:22:13

标签: opengl

我正在尝试在OpenGL中为一个应用程序创建一个矩形,锋利的光源。我的想法是创建一个聚光灯,并以某种方式将阴影的形状掩盖成一个矩形,当然,面具必须通过相机隐形。当我试图实现这个想法时,事实证明OpenGL只会跳过相机外的渲染对象,尽管相机外的光源仍然有效。这使我无法创造出我想要的效果,我想知道你们中是否有人遇到过类似的问题。

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

点亮0,0,5

目标对象位于0,0,0

屏蔽对象(与x轴平行的简单四边形)位于0,0,3。

当相机处于0,0,4时,光线穿过遮罩物体并在目标物体上留下一个矩形形状(这就是我想要的),但我也可以看到遮罩物体!(而我需要掩码对象不可见)

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

3 个答案:

答案 0 :(得分:0)

我的猜测是从聚光灯开始,但是分开角度计算: *在YZ平面上投影L矢量以计算X轴上的角度 *在XZ平面上投影L矢量以计算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投影在世界空间中完成。如果您希望能够旋转灯光矩形,则可能需要引入指向灯光右侧的矢量。 因此,您可以在光的坐标系中获得片段坐标,并且可以通过它来决定是否对片段进行着色。

答案 1 :(得分:0)

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

enter image description here

当然,这不会模拟矩形区域光源,只是一个受限于矩形区域的点光源。

使用这种方法,您必须指定视图&光源的投影矩阵;其中视图矩阵基本上由光线位置&的'look at'生成。它的方向;投影矩阵用您想要的水平和垂直方向编码透视投影。垂直'视野'。

如果你只想要一个矩形区域,你甚至不需要纹理;一个简单的顶点/片段着色器对可能如下所示: (顶点着色器基本上将位置转换为灯光的剪辑空间,片段着色器执行剪裁&计算朗伯阴影,如果片段在光锥体内)

#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 );
}

答案 2 :(得分: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/