Fogging by depth buffer value, as Direct3D 9's documentation suggests for IMVU's projections, looks nothing like the client; fogging by view depth matched it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
147 lines
5.2 KiB
HLSL
147 lines
5.2 KiB
HLSL
// IMVU's Direct3D 9 fixed-function look, as its renderer sets it up (docs/interface.md):
|
|
// - per-vertex (Gouraud) lighting, with the vertex color as diffuse and ambient material (white without one): the
|
|
// environment's ambient light, plus each light's color times max(0, N.L); no specular or emissive light;
|
|
// - textures: none (the lit color), one (times the lit color), or two (the second blended over the first by its
|
|
// alpha, then times the lit color), both on the vertex's texture coordinates with their own transforms;
|
|
// - an alpha test passing pixels whose alpha is greater than the reference, then linear fog by view depth (checked
|
|
// against the client: fogging by depth buffer value instead looks nothing like it).
|
|
//
|
|
// Particles (vs_sprite) are Direct3D 9 point sprites there: camera-facing squares, viewport height / distance pixels
|
|
// across (1 to 64), textured over the whole square; here each is drawn as an instance of a 4-vertex strip.
|
|
//
|
|
// Compiled for each combination of the vertex streams present (HAS_NORMALS, HAS_TEX_COORDS, HAS_COLORS) and the
|
|
// number of textures (TEXTURES).
|
|
|
|
cbuffer Constants
|
|
{
|
|
row_major float4x4 g_World;
|
|
row_major float4x4 g_View;
|
|
row_major float4x4 g_Projection;
|
|
float4 g_TexTransform0[2]; // u' = dot((u, v, 1), [0].xyz), v' = dot((u, v, 1), [1].xyz)
|
|
float4 g_TexTransform1[2];
|
|
float4 g_Ambient; // rgb
|
|
float4 g_LightColor[8]; // rgb, a: enabled
|
|
float4 g_LightVector[8]; // xyz: position, or direction toward the light; w: 1 if directional
|
|
float4 g_FogColor; // rgb
|
|
float4 g_Fog; // x: start, y: end, z: on
|
|
float4 g_Flags; // x: lighting on, y: alpha test on, z: alpha reference (0-255)
|
|
float4 g_Viewport; // xy: size in pixels
|
|
};
|
|
|
|
struct VSInput
|
|
{
|
|
float3 Position : ATTRIB0;
|
|
#if HAS_NORMALS
|
|
float3 Normal : ATTRIB1;
|
|
#endif
|
|
#if HAS_TEX_COORDS
|
|
float2 TexCoord : ATTRIB2;
|
|
#endif
|
|
#if HAS_COLORS
|
|
float4 Color : ATTRIB3; // bytes B, G, R, A
|
|
#endif
|
|
};
|
|
|
|
struct PSInput
|
|
{
|
|
float4 Position : SV_POSITION;
|
|
float4 Color : COLOR0;
|
|
float2 TexCoord0 : TEXCOORD0;
|
|
float2 TexCoord1 : TEXCOORD1;
|
|
float Depth : TEXCOORD2;
|
|
};
|
|
|
|
void vs_main(in VSInput input, out PSInput output)
|
|
{
|
|
float4 world = mul(float4(input.Position, 1.0), g_World);
|
|
float4 view = mul(world, g_View);
|
|
output.Position = mul(view, g_Projection);
|
|
output.Depth = abs(view.z);
|
|
|
|
float4 material = float4(1.0, 1.0, 1.0, 1.0);
|
|
#if HAS_COLORS
|
|
material = input.Color.zyxw;
|
|
#endif
|
|
float4 color = material;
|
|
if (g_Flags.x > 0.5)
|
|
{
|
|
float3 normal = float3(0.0, 0.0, 0.0);
|
|
#if HAS_NORMALS
|
|
normal = normalize(mul(input.Normal, (float3x3)g_World));
|
|
#endif
|
|
float3 lit = g_Ambient.rgb * material.rgb;
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
if (g_LightColor[i].a > 0.5)
|
|
{
|
|
float3 toLight = g_LightVector[i].w > 0.5 ? g_LightVector[i].xyz : g_LightVector[i].xyz - world.xyz;
|
|
lit += g_LightColor[i].rgb * material.rgb * max(0.0, dot(normal, normalize(toLight)));
|
|
}
|
|
}
|
|
color = float4(saturate(lit), material.a);
|
|
}
|
|
output.Color = color;
|
|
|
|
float3 uv = float3(0.0, 0.0, 1.0);
|
|
#if HAS_TEX_COORDS
|
|
uv.xy = input.TexCoord;
|
|
#endif
|
|
output.TexCoord0 = float2(dot(uv, g_TexTransform0[0].xyz), dot(uv, g_TexTransform0[1].xyz));
|
|
output.TexCoord1 = float2(dot(uv, g_TexTransform1[0].xyz), dot(uv, g_TexTransform1[1].xyz));
|
|
}
|
|
|
|
struct SpriteInput
|
|
{
|
|
float3 Position : ATTRIB0;
|
|
float4 Color : ATTRIB3; // bytes B, G, R, A
|
|
uint Corner : SV_VertexID;
|
|
};
|
|
|
|
void vs_sprite(in SpriteInput input, out PSInput output)
|
|
{
|
|
float4 view = mul(mul(float4(input.Position, 1.0), g_World), g_View);
|
|
float4 clip = mul(view, g_Projection);
|
|
float size = clamp(g_Viewport.y / max(length(view.xyz), 1e-4), 1.0, 64.0);
|
|
float2 corner = float2((input.Corner & 1) != 0 ? 1.0 : -1.0, (input.Corner & 2) != 0 ? -1.0 : 1.0);
|
|
clip.xy += corner * size / g_Viewport.xy * clip.w; // half the size each way, in clip space
|
|
output.Position = clip;
|
|
output.Depth = abs(view.z);
|
|
|
|
// No normals: lit by the ambient light alone.
|
|
float4 material = input.Color.zyxw;
|
|
output.Color = g_Flags.x > 0.5 ? float4(saturate(g_Ambient.rgb * material.rgb), material.a) : material;
|
|
output.TexCoord0 = float2(corner.x * 0.5 + 0.5, 0.5 - corner.y * 0.5);
|
|
output.TexCoord1 = output.TexCoord0;
|
|
}
|
|
|
|
#if TEXTURES >= 1
|
|
Texture2D g_Texture0;
|
|
SamplerState g_Texture0_sampler;
|
|
#endif
|
|
#if TEXTURES >= 2
|
|
Texture2D g_Texture1;
|
|
SamplerState g_Texture1_sampler;
|
|
#endif
|
|
|
|
float4 ps_main(in PSInput input) : SV_Target
|
|
{
|
|
float4 color = input.Color;
|
|
#if TEXTURES == 1
|
|
color *= g_Texture0.Sample(g_Texture0_sampler, input.TexCoord0);
|
|
#elif TEXTURES == 2
|
|
float4 first = g_Texture0.Sample(g_Texture0_sampler, input.TexCoord0);
|
|
float4 second = g_Texture1.Sample(g_Texture1_sampler, input.TexCoord1);
|
|
color *= lerp(first, second, second.a);
|
|
#endif
|
|
if (g_Flags.y > 0.5 && !(color.a * 255.0 > g_Flags.z))
|
|
{
|
|
discard;
|
|
}
|
|
if (g_Fog.z > 0.5)
|
|
{
|
|
float visible = saturate((g_Fog.y - input.Depth) / max(g_Fog.y - g_Fog.x, 1e-6));
|
|
color.rgb = lerp(g_FogColor.rgb, color.rgb, visible);
|
|
}
|
|
return color;
|
|
}
|