native render: transliterate D3D8 fixed-function pipeline (unit A)
- Texture coordinates: TEXCOORDINDEX generation (camera-space normal / position / reflection vector, LOCALVIEWER) and D3DTS_TEXTUREn under TEXTURETRANSFORMFLAGS (incl. PROJECTED) now run in native.vert; the RecordingDevice CPU uv baking and the sphere-map / multiplicative-shadow shader hacks are removed. Godot consumer uses Render3DStageTexcoords. - Lighting: 8 D3D lights in camera space, 1/(a0+a1d+a2d^2) without clamp, spot cone theta/2 phi/2 with falloff, diffuse/ambient/emissive material sources, NORMALIZENORMALS, inverse-transpose normal matrix. - Alpha test compares 8-bit alpha against ALPHAREF with ALPHAFUNC. - Back-buffer Clear() is recorded in draw order and executed with vkCmdClearAttachments; render pass clears to 0xff000000. - D3DVIEWPORT8 (incl. MinZ/MaxZ) is dynamic per-draw state. - Missing vertex diffuse is opaque white; alpha blend mirrors SRC/DESTBLEND. - CScreen::ms_clearDepth / ms_diffuseColor initialised to 40250's 1.0f / 0xffffffff (were zero; exposed once clears were executed). - Capture format v8; --screenshot-out FILE.bmp swapchain readback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
425e6f37fb
commit
5285ca85fd
+98
-61
@@ -19,40 +19,75 @@ layout(set = 1, binding = 0, std430) readonly buffer BonePalette {
|
||||
|
||||
layout(push_constant) uniform DrawConstants {
|
||||
mat4 mvp;
|
||||
vec4 tint_color;
|
||||
vec4 ambient_emissive;
|
||||
vec4 light_dir;
|
||||
vec4 light_diffuse;
|
||||
vec4 params; // bone base + 1 (0 = none), pretransformed, UI mask, unused
|
||||
} draw;
|
||||
|
||||
// D3D8 fixed-function vertex state; see FixedFunctionState in main.cpp. Lights are in camera space.
|
||||
layout(set = 1, binding = 1, std430) readonly buffer FixedFunctionState {
|
||||
uvec4 stage0_color;
|
||||
uvec4 stage0_alpha;
|
||||
uvec4 stage1_color;
|
||||
uvec4 stage1_alpha;
|
||||
uvec4 stage_color[2]; // op, arg1, arg2, D3DTSS_TEXCOORDINDEX
|
||||
uvec4 stage_alpha[2]; // op, arg1, arg2, D3DTSS_TEXTURETRANSFORMFLAGS
|
||||
vec4 fog_color;
|
||||
vec4 fog_params;
|
||||
vec4 texture_factor;
|
||||
mat4 world_view;
|
||||
uvec4 flags;
|
||||
mat4 world;
|
||||
uvec4 lighting_flags;
|
||||
mat4 normal_matrix;
|
||||
uvec4 lighting_flags; // LIGHTING, COLORVERTEX, vertex has diffuse, NORMALIZENORMALS
|
||||
uvec4 material_sources; // DIFFUSE, AMBIENT, EMISSIVE material source, LOCALVIEWER
|
||||
uvec4 alpha_test;
|
||||
vec4 material_diffuse;
|
||||
vec4 material_ambient;
|
||||
vec4 material_emissive;
|
||||
vec4 global_ambient;
|
||||
vec4 light_position_type[2];
|
||||
vec4 light_direction_range[2];
|
||||
vec4 light_diffuse[2];
|
||||
vec4 light_ambient[2];
|
||||
vec4 light_attenuation[2];
|
||||
vec4 light_spot[2];
|
||||
mat4 texture_matrix[2];
|
||||
vec4 light_position_type[8];
|
||||
vec4 light_direction_range[8];
|
||||
vec4 light_diffuse[8];
|
||||
vec4 light_ambient[8];
|
||||
vec4 light_attenuation[8];
|
||||
vec4 light_spot[8];
|
||||
} fixed_state;
|
||||
|
||||
// D3DMCS_COLOR1 picks the vertex diffuse color when COLORVERTEX is on and the vertex has one.
|
||||
vec4 material_color(uint source, vec4 material) {
|
||||
if (fixed_state.lighting_flags.y != 0u && fixed_state.lighting_flags.z != 0u && source == 1u)
|
||||
return in_color;
|
||||
return material;
|
||||
}
|
||||
|
||||
// D3D8 texture coordinate processing: D3DTSS_TEXCOORDINDEX generation, then D3DTS_TEXTUREn.
|
||||
vec2 stage_texcoord(int stage, bool pretransformed, vec3 eye, vec3 n) {
|
||||
uint tci = fixed_state.stage_color[stage].w;
|
||||
uint gen = tci & 0xFFFF0000u;
|
||||
uint set_index = tci & 0xFFFFu;
|
||||
vec4 coord = vec4(0.0, 0.0, 1.0, 0.0);
|
||||
if (gen == 0u || pretransformed) {
|
||||
if (set_index == 0u) coord.xy = in_uv;
|
||||
else if (set_index == 1u) coord.xy = in_mask_uv;
|
||||
} else if (gen == 0x10000u) { // CAMERASPACENORMAL
|
||||
coord = vec4(n, 1.0);
|
||||
} else if (gen == 0x20000u) { // CAMERASPACEPOSITION
|
||||
coord = vec4(eye, 1.0);
|
||||
} else if (gen == 0x30000u) { // CAMERASPACEREFLECTIONVECTOR
|
||||
vec3 e = fixed_state.material_sources.w != 0u
|
||||
? (length(eye) > 0.0 ? -normalize(eye) : vec3(0.0)) : vec3(0.0, 0.0, -1.0);
|
||||
coord = vec4(2.0 * dot(e, n) * n - e, 1.0);
|
||||
}
|
||||
uint transform_flags = fixed_state.stage_alpha[stage].w;
|
||||
uint elements = transform_flags & 0xFFu;
|
||||
vec4 r = coord;
|
||||
if (elements != 0u)
|
||||
r = fixed_state.texture_matrix[stage] * coord;
|
||||
if ((transform_flags & 0x100u) != 0u && elements >= 2u && elements <= 4u && r[elements - 1u] != 0.0)
|
||||
r.xy /= r[elements - 1u];
|
||||
return r.xy;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 pos = in_position;
|
||||
vec3 nrm = in_normal;
|
||||
if (draw.light_diffuse.w > 0.5) {
|
||||
uint bone_base = uint(draw.light_diffuse.w - 0.5);
|
||||
if (draw.params.x > 0.5) {
|
||||
uint bone_base = uint(draw.params.x - 0.5);
|
||||
vec3 skinned_pos = vec3(0.0);
|
||||
vec3 skinned_nrm = vec3(0.0);
|
||||
float total_w = 0.0;
|
||||
@@ -70,72 +105,74 @@ void main() {
|
||||
nrm = skinned_nrm;
|
||||
}
|
||||
}
|
||||
bool pretransformed = draw.params.y > 0.5;
|
||||
// D3D row-vector matrices are uploaded row-major. GLSL reads the bytes as their transpose.
|
||||
gl_Position = draw.mvp * vec4(pos, 1.0);
|
||||
if (draw.light_diffuse.w < -0.5) {
|
||||
if (pretransformed) {
|
||||
float clip_w = in_rhw > 0.0 ? 1.0 / in_rhw : 1.0;
|
||||
gl_Position.xyz *= clip_w;
|
||||
gl_Position.w = clip_w;
|
||||
}
|
||||
gl_Position.y = -gl_Position.y;
|
||||
vec3 n = length(nrm) > 1e-4 ? normalize(nrm) : vec3(0.0, 0.0, 1.0);
|
||||
|
||||
// A vertex without D3DFVF_DIFFUSE carries opaque white (upload_geometry).
|
||||
out_color = in_color;
|
||||
if (fixed_state.lighting_flags.x != 0u) {
|
||||
vec3 world_pos = (fixed_state.world * vec4(pos, 1.0)).xyz;
|
||||
vec3 world_normal = normalize(mat3(fixed_state.world) * n);
|
||||
vec3 mat_diffuse = (fixed_state.lighting_flags.y != 0u && fixed_state.lighting_flags.w == 1u)
|
||||
? in_color.rgb : draw.tint_color.rgb;
|
||||
vec3 mat_ambient = (fixed_state.lighting_flags.y != 0u && fixed_state.lighting_flags.z == 1u)
|
||||
? in_color.rgb : fixed_state.material_ambient.rgb;
|
||||
if (fixed_state.flags.x == 0u) {
|
||||
// 2D UI batch.
|
||||
out_uv = in_uv;
|
||||
out_mask_uv = in_mask_uv;
|
||||
out_fog = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 eye = (fixed_state.world_view * vec4(pos, 1.0)).xyz;
|
||||
vec3 n = mat3(fixed_state.normal_matrix) * nrm;
|
||||
if (fixed_state.lighting_flags.w != 0u && length(n) > 0.0)
|
||||
n = normalize(n);
|
||||
|
||||
if (fixed_state.lighting_flags.x != 0u && !pretransformed) {
|
||||
vec4 mat_diffuse = material_color(fixed_state.material_sources.x, fixed_state.material_diffuse);
|
||||
vec4 mat_ambient = material_color(fixed_state.material_sources.y, fixed_state.material_ambient);
|
||||
vec4 mat_emissive = material_color(fixed_state.material_sources.z, fixed_state.material_emissive);
|
||||
vec3 ambient_sum = fixed_state.global_ambient.rgb;
|
||||
vec3 diffuse_sum = vec3(0.0);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int type = int(fixed_state.light_position_type[i].w + 0.5);
|
||||
if (type == 0) continue;
|
||||
vec3 L;
|
||||
float strength = 1.0;
|
||||
if (type == 3) {
|
||||
L = normalize(-fixed_state.light_direction_range[i].xyz);
|
||||
} else {
|
||||
vec3 to_light = fixed_state.light_position_type[i].xyz - world_pos;
|
||||
float distance_to_light = length(to_light);
|
||||
if (distance_to_light > fixed_state.light_direction_range[i].w || distance_to_light < 0.0001)
|
||||
if (type == 3) { // D3DLIGHT_DIRECTIONAL
|
||||
L = -fixed_state.light_direction_range[i].xyz;
|
||||
} else { // D3DLIGHT_POINT / D3DLIGHT_SPOT
|
||||
vec3 to_light = fixed_state.light_position_type[i].xyz - eye;
|
||||
float d = length(to_light);
|
||||
if (d > fixed_state.light_direction_range[i].w)
|
||||
continue;
|
||||
L = to_light / distance_to_light;
|
||||
L = d > 0.0 ? to_light / d : vec3(0.0);
|
||||
vec4 attenuation = fixed_state.light_attenuation[i];
|
||||
float denominator = attenuation.x + attenuation.y * distance_to_light +
|
||||
attenuation.z * distance_to_light * distance_to_light;
|
||||
strength = denominator > 0.0001 ? min(1.0 / denominator, 1.0) : 1.0;
|
||||
float denominator = attenuation.x + attenuation.y * d + attenuation.z * d * d;
|
||||
strength = denominator != 0.0 ? 1.0 / denominator : 1.0;
|
||||
if (type == 2) {
|
||||
vec3 spot_dir = normalize(fixed_state.light_direction_range[i].xyz);
|
||||
float cosine = dot(-L, spot_dir);
|
||||
float inner = cos(fixed_state.light_spot[i].x * 0.5);
|
||||
float outer = cos(fixed_state.light_spot[i].y * 0.5);
|
||||
float cone = clamp((cosine - outer) / max(inner - outer, 0.0001), 0.0, 1.0);
|
||||
strength *= pow(cone, max(attenuation.w, 0.0001));
|
||||
float rho = dot(-L, fixed_state.light_direction_range[i].xyz);
|
||||
float cos_theta = fixed_state.light_spot[i].x;
|
||||
float cos_phi = fixed_state.light_spot[i].y;
|
||||
float spot = rho > cos_theta ? 1.0 : (rho <= cos_phi ? 0.0 :
|
||||
pow((rho - cos_phi) / (cos_theta - cos_phi), attenuation.w));
|
||||
strength *= spot;
|
||||
}
|
||||
}
|
||||
ambient_sum += fixed_state.light_ambient[i].rgb * strength;
|
||||
diffuse_sum += fixed_state.light_diffuse[i].rgb * max(dot(world_normal, L), 0.0) * strength;
|
||||
diffuse_sum += fixed_state.light_diffuse[i].rgb * max(dot(n, L), 0.0) * strength;
|
||||
}
|
||||
vec3 lit = fixed_state.material_emissive.rgb + mat_ambient * ambient_sum + mat_diffuse * diffuse_sum;
|
||||
float alpha = (fixed_state.lighting_flags.y != 0u && fixed_state.lighting_flags.w == 1u)
|
||||
? in_color.a : draw.tint_color.a;
|
||||
out_color = vec4(clamp(lit, 0.0, 1.0), alpha);
|
||||
}
|
||||
out_uv = in_uv;
|
||||
if (draw.light_dir.w > 1.001) {
|
||||
vec3 view_normal = normalize(mat3(fixed_state.world_view) * n);
|
||||
vec3 view_pos = (fixed_state.world_view * vec4(pos, 1.0)).xyz;
|
||||
vec3 view_dir = normalize(-view_pos);
|
||||
vec3 reflected = reflect(-view_dir, view_normal);
|
||||
out_mask_uv = reflected.xy * vec2(0.5, -0.5) + vec2(0.5);
|
||||
} else {
|
||||
out_mask_uv = in_mask_uv;
|
||||
vec3 lit = mat_emissive.rgb + mat_ambient.rgb * ambient_sum + mat_diffuse.rgb * diffuse_sum;
|
||||
out_color = vec4(clamp(lit, 0.0, 1.0), clamp(mat_diffuse.a, 0.0, 1.0));
|
||||
}
|
||||
|
||||
out_uv = stage_texcoord(0, pretransformed, eye, n);
|
||||
out_mask_uv = stage_texcoord(1, pretransformed, eye, n);
|
||||
|
||||
out_fog = 1.0;
|
||||
if (fixed_state.flags.x != 0u && fixed_state.flags.w != 0u && draw.light_diffuse.w >= -0.5) {
|
||||
vec3 eye = (fixed_state.world_view * vec4(pos, 1.0)).xyz;
|
||||
if (fixed_state.flags.w != 0u && !pretransformed) {
|
||||
float distance_to_eye = fixed_state.fog_params.w > 0.5 ? length(eye) : abs(eye.z);
|
||||
if (fixed_state.flags.w == 3u) {
|
||||
float span = fixed_state.fog_params.y - fixed_state.fog_params.x;
|
||||
|
||||
Reference in New Issue
Block a user