簡體   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