// 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, 2048.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; }