[英]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.