我正在编写一个着色器来通过绘制阴影圆圈在点精灵上渲染球体,并且需要编写深度分量和颜色,以便彼此靠近的球体正确相交。
我正在使用类似于Johna Holwerda编写的代码:
void PS_ShowDepth(VS_OUTPUT input, out float4 color: COLOR0,out float depth : DEPTH)
{
float dist = length (input.uv - float2 (0.5f, 0.5f)); //get the distance form the center of the point-sprite
float alpha = saturate(sign (0.5f - dist));
sphereDepth = cos (dist * 3.14159) * sphereThickness * particleSize; //calculate how thick the sphere should be; sphereThickness is a variable.
depth = saturate (sphereDepth + input.color.w); //input.color.w represents the depth value of the pixel on the point-sprite
color = float4 (depth.xxx ,alpha ); //or anything else you might need in future passes
}
该链接上的视频很好地了解了我所追求的效果:在点精灵上绘制的那些球体正确相交。我在下面添加了图片来说明。
我可以很好地计算点精灵本身的深度。但是,我不确定要计算球体在像素处的厚度,以便将其添加到精灵的深度,从而给出最终的深度值。(上面的代码使用一个变量而不是计算它。)
我已经断断续续地研究了几个星期,但还没有弄明白——我敢肯定这很简单,但这是我的大脑还没有动过的东西。
Direct3D 9 的点精灵大小以像素为单位计算,我的精灵有几种大小 - 都是由于距离造成的衰减(我实现了与在我的顶点着色器中用于点大小计算的旧固定函数管道相同的算法)以及由于什么精灵代表。
如何从像素着色器中的数据(精灵位置、精灵深度、原始世界空间半径、屏幕上的像素半径、相关像素到精灵中心的标准化距离)转换为深度值?简单地将精灵大小与深度坐标中的球体厚度 的部分解决方案很好 - 可以通过与中心的归一化距离进行缩放以获得像素处的球体厚度。
我使用 Direct3D 9 和 HLSL 以及着色器模型 3 作为 SM 上限。
在图片中
为了演示该技术,以及我遇到问题的地方:
从两个点精灵开始,在像素着色器中在每个精灵上画一个圆圈,使用剪辑删除圆圈边界之外的片段:
一个将渲染在另一个之上,因为它们毕竟是平面。
现在,使着色器更高级,并绘制圆形,就好像它是一个球体一样,带有照明。请注意,即使平面精灵看起来是 3D 的,它们仍然会完全在另一个之前绘制,因为这是一种错觉:它们仍然是平面的。
(以上很简单;这是我遇到问题的最后一步,我正在询问如何实现。)
现在,像素着色器不再只写颜色值,它也应该写深度:
void SpherePS (...any parameters...
out float4 oBackBuffer : COLOR0,
out float oDepth : DEPTH0 <- now also writing depth
)
{
请注意,现在当球体之间的距离小于它们的半径时,球体相交:
如何计算正确的深度值以完成最后一步?
编辑/注释
一些人评论说,真实的球体会因透视而变形,这在屏幕边缘可能特别明显,所以我应该使用不同的技术。首先,感谢您指出这一点,这不一定是显而易见的,对未来的读者有好处!其次,我的目标不是渲染透视正确的球体,而是快速渲染数百万个数据点,并且在视觉上我认为类似球体的对象看起来比平面精灵更好,并且也更好地显示空间位置。轻微的失真或没有失真都没有关系。如果您观看演示视频,您可以看到它是一个有用的可视化工具。我不想渲染实际的球体网格,因为与简单的硬件生成的点精灵相比,三角形数量众多。我真的很想使用点精灵的技术,我只是想扩展现有的演示技术以计算正确的深度值,在演示中它是作为变量传入的,没有来源说明它是如何派生的。