简体   繁体   English

在OpenGL中创建矩形光源?

[英]Creating a rectangular light source in OpenGL?

I am trying to create a rectangular, sharp-edge light source in OpenGL for one application. 我正在尝试为一个应用程序在OpenGL中创建一个矩形,锐利的光源。 My idea is to create a spot light and somehow mask the shape of the shade into a rectangle, the mask of course has to be invisible through camera. 我的想法是创建一个聚光灯,并以某种方式将阴影的形状蒙版为矩形,当然,蒙版必须通过相机看不见。 When I was trying to implement this idea, it turns out that OpenGL will just skip rendering objects outside the camera, although lighting source outside camera is still valid. 当我尝试实现此想法时,事实证明OpenGL会跳过摄像机外部的渲染对象,尽管摄像机外部的光源仍然有效。 This has prevented me from creating the effect I wanted and I am wondering if any of you have come across similar problems before. 这使我无法产生想要的效果,我想知道以前是否有人遇到过类似的问题。

To make my question more specific, consider the following case of my question: 为了使我的问题更具体,请考虑以下情况:

spot light at 0,0,5 0,0,5的聚光灯

target object at 0,0,0 目标对象位于0,0,0

mask object (a simple quad parallel to x-axis) at 0,0,3. 在0,0,3处遮罩对象(平行于x轴的简单四边形)。

When camera is at 0,0,4, light passes through mask object and leaves a rectangular shape on the target object (which is what I wanted), but I can also see the mask object!(while I need the mask object to be invisible) 当相机位于0,0,4时,光线穿过蒙版对象并在目标对象上留下矩形形状(这是我想要的),但是我也可以看到蒙版对象!(同时我需要将蒙版对象设置为无形)

When I move the camera closer to the target object, say 0,0,2. 当我将相机移近目标物体时,说0,0,2。 The mask object is behind the camera and therefore invisible. 遮罩对象在相机后面,因此是不可见的。 However, since it's invisible, OpenGL stopped rendering it and therefore the mask object does not have any effect on the target object, and the light shade is still round! 但是,由于它是不可见的,OpenGL停止了对其进行渲染,因此mask对象对目标对象没有任何影响,并且阴影仍然是圆形的!

My guess would be to start from a spot light, but separating the angle calculation: * Project the L vector on the YZ plane to calculate the angle on the X axis * Project the L vector on the XZ plane to calculate the angle on the Y axis 我的猜测是从聚光灯开始,但分开进行角度计算:*将L向量投影在YZ平面上以计算X轴上的角度*将L向量投影在XZ平面上以计算Y上的角度轴

A very naive implementation of this could be (GLSL): 一个非常幼稚的实现可能是(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.
}

Again, this is very naive. 再次,这是非常幼稚的。 For instance, the X/Y projection is done in world-space. 例如,X / Y投影是在世界空间中完成的。 If you want to be able to rotate the light rectangle, you might have to introduce a vector pointing to the right of the light. 如果要旋转光源矩形,则可能必须引入指向光源右侧的向量。 Thus, you'll be able to get the fragment coordinate in the light's coordinate frame, and with this, you can decide whether to shade the fragment or not. 因此,您将能够在光源的坐标系中获取片段坐标,并以此决定是否着色片段。

One solution might be adapting the calculations used for projective texture lookups to simulate a rectangular light source. 一种解决方案可能是调整用于投影纹理查找的计算以模拟矩形光源。 You did not specify which OpenGL version you're using, but projective texture lookups can even be achieved with the fixed function pipeline - although they're arguably easier to do in a shader. 您没有指定要使用的OpenGL版本,但投射式纹理查找甚至可以通过固定功能管线实现-尽管可以说它们在着色器中更容易实现。

在此处输入图片说明

Of course, this would not simulate a rectangular area light source, just a point light source that is constrained to a rectangular region. 当然,这将不会模拟矩形区域光源,而只能模拟约束到矩形区域的点光源。

Using this approach, you'd have to specify view & projection matrices for the light source; 使用这种方法,您必须为光源指定视图和投影矩阵。 where the view matrix is essentially generated by a 'look at' with the light position & it's direction; 视点矩阵基本上是由“看”到灯光位置及其方向生成的; the projection matrix encodes a perspective projection with your desired horizontal & vertical 'field of view'. 投影矩阵使用您所需的水平和垂直“视场”对透视投影进行编码。

If you just want a rectangular area, you don't even need a texture; 如果只需要一个矩形区域,则甚至不需要纹理。 A simple vertex/ fragment shader pair could look like this: ( the vertex shader basically transforms the position to the light's clip space, the fragment shader performs the clipping & computes a lambert shading if the fragment is inside the light frustum ) 一个简单的顶点/片段着色器对可能看起来像这样:(顶点着色器基本上将位置转换为光源的剪贴空间,片段着色器执行裁剪并计算片段(如果片段位于视锥内部)

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

There are a lot of good papers on this, Brian Karis wrote about it in 2013 (in regards to UE4) here: 关于这一点,有很多好的论文,Brian Karis在2013年(关于UE4)写到了这里:

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

And more recently Michal Drobot wrote an article about area lights in GPU Pro 5. 最近,Michal Drobot撰写了一篇有关GPU Pro 5中区域照明的文章。

If you are using a metalness workflow you can also crank up the roughness as an approximation to area lighting, a technique introduced by Tri-Ace: 如果您使用的是金属化工作流程,您还可以提高粗糙度,以近似于区域照明,这是Tri-Ace引入的一种技术:

http://www.fxguide.com/featured/game-environments-parta-remember-me-rendering/ 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