我正在尝试在OpenGL中为一个应用程序创建一个矩形,锋利的光源。我的想法是创建一个聚光灯,并以某种方式将阴影的形状掩盖成一个矩形,当然,面具必须通过相机隐形。当我试图实现这个想法时,事实证明OpenGL只会跳过相机外的渲染对象,尽管相机外的光源仍然有效。这使我无法创造出我想要的效果,我想知道你们中是否有人遇到过类似的问题。
为了使我的问题更具体,请考虑以下我的问题:
点亮0,0,5
目标对象位于0,0,0
屏蔽对象(与x轴平行的简单四边形)位于0,0,3。
当相机处于0,0,4时,光线穿过遮罩物体并在目标物体上留下一个矩形形状(这就是我想要的),但我也可以看到遮罩物体!(而我需要掩码对象不可见)
当我将相机移近目标物体时,请说0,0,2。遮罩物体位于相机后面,因此不可见。但是,由于它不可见,OpenGL停止渲染它,因此遮罩对象对目标对象没有任何影响,并且灯罩仍然是圆形的!
答案 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版本,但是使用固定功能管道甚至可以实现投影纹理查找 - 虽然在着色器中它们可以说更容易。
当然,这不会模拟矩形区域光源,只是一个受限于矩形区域的点光源。
使用这种方法,您必须指定视图&光源的投影矩阵;其中视图矩阵基本上由光线位置&的'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/