Align hardware lighting with software (#221)

This commit is contained in:
Anders Jenbo 2025-06-03 02:16:33 +02:00 committed by GitHub
parent f08aec7438
commit 2affbdfcc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1588 additions and 1052 deletions

View File

@ -1,9 +1,11 @@
#include "ShaderIndex.h" #include "ShaderIndex.h"
#include "d3drmrenderer.h"
#include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_sdl3gpu.h"
#include "ddraw_impl.h" #include "ddraw_impl.h"
#include "miniwin.h" #include "miniwin.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <cstddef>
static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device) static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device)
{ {
@ -33,21 +35,36 @@ static SDL_GPUGraphicsPipeline* InitializeGraphicsPipeline(SDL_GPUDevice* device
vertexBufferDescs[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX; vertexBufferDescs[0].input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
vertexBufferDescs[0].instance_step_rate = 0; vertexBufferDescs[0].instance_step_rate = 0;
SDL_GPUVertexAttribute vertexAttrs[3] = {}; SDL_GPUVertexAttribute vertexAttrs[6] = {};
vertexAttrs[0].location = 0; vertexAttrs[0].location = 0;
vertexAttrs[0].buffer_slot = 0; vertexAttrs[0].buffer_slot = 0;
vertexAttrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; vertexAttrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
vertexAttrs[0].offset = 0; vertexAttrs[0].offset = offsetof(PositionColorVertex, position);
vertexAttrs[1].location = 1; vertexAttrs[1].location = 1;
vertexAttrs[1].buffer_slot = 0; vertexAttrs[1].buffer_slot = 0;
vertexAttrs[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; vertexAttrs[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
vertexAttrs[1].offset = sizeof(float) * 3; vertexAttrs[1].offset = offsetof(PositionColorVertex, normals);
vertexAttrs[2].location = 2; vertexAttrs[2].location = 2;
vertexAttrs[2].buffer_slot = 0; vertexAttrs[2].buffer_slot = 0;
vertexAttrs[2].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM; vertexAttrs[2].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM;
vertexAttrs[2].offset = sizeof(float) * 6; vertexAttrs[2].offset = offsetof(PositionColorVertex, colors);
vertexAttrs[3].location = 3;
vertexAttrs[3].buffer_slot = 0;
vertexAttrs[3].format = SDL_GPU_VERTEXELEMENTFORMAT_UINT;
vertexAttrs[3].offset = offsetof(PositionColorVertex, texId);
vertexAttrs[4].location = 4;
vertexAttrs[4].buffer_slot = 0;
vertexAttrs[4].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
vertexAttrs[4].offset = offsetof(PositionColorVertex, texCoord);
vertexAttrs[5].location = 5;
vertexAttrs[5].buffer_slot = 0;
vertexAttrs[5].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT;
vertexAttrs[5].offset = offsetof(PositionColorVertex, shininess);
SDL_GPUVertexInputState vertexInputState = {}; SDL_GPUVertexInputState vertexInputState = {};
vertexInputState.vertex_buffer_descriptions = vertexBufferDescs; vertexInputState.vertex_buffer_descriptions = vertexBufferDescs;

View File

@ -1,21 +1,28 @@
struct VS_Input struct VS_Input
{ {
float3 Position : TEXCOORD0; float3 Position : POSITION;
float3 Normal : TEXCOORD1; float3 Normal : NORMAL0;
float4 Color : TEXCOORD2; float4 Color : COLOR0;
uint TexId : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float Shininess : TEXCOORD2;
}; };
struct FS_Input struct FS_Input
{ {
float4 Color : TEXCOORD0; float4 Position : SV_POSITION;
float3 Normal : TEXCOORD1; float3 Normal : NORMAL0;
float4 Position : SV_Position; float4 Color : COLOR0;
uint TexId : TEXCOORD0;
float2 TexCoord : TEXCOORD1;
float Shininess : TEXCOORD2;
float3 WorldPosition : TEXCOORD3;
}; };
struct FS_Output struct FS_Output
{ {
float4 Color : SV_Target0; float4 Color : SV_Target0;
float Depth : SV_Depth; float Depth : SV_Depth;
}; };
struct SceneLight { struct SceneLight {

View File

@ -8,8 +8,13 @@ cbuffer ViewportUniforms : register(b0, space1)
FS_Input main(VS_Input input) FS_Input main(VS_Input input)
{ {
FS_Input output; FS_Input output;
output.TexCoord = input.TexCoord;
output.Color = input.Color; output.Color = input.Color;
output.Normal = input.Normal; output.Normal = input.Normal;
output.Position = mul(perspective, float4(input.Position, 1.0)); output.Position = mul(perspective, float4(input.Position, 1.0));
output.WorldPosition = input.Position;
output.TexId = input.TexId;
output.Shininess = input.Shininess;
return output; return output;
} }

View File

@ -9,53 +9,50 @@ cbuffer LightBuffer : register(b0, space3)
FS_Output main(FS_Input input) FS_Output main(FS_Input input)
{ {
FS_Output output; FS_Output output;
float3 normal = normalize(input.Normal);
float3 fragPos = input.Position.xyz / input.Position.w;
float3 viewPos = float3(0, 0, 0); float3 diffuse = float3(0, 0, 0);
float3 viewDir = normalize(viewPos - fragPos); float3 specular = float3(0, 0, 0);
float3 result = float3(0, 0, 0);
const float shininess = 20.0; // All materials use this in Isle
for (int i = 0; i < lightCount; ++i) { for (int i = 0; i < lightCount; ++i) {
float3 lightColor = lights[i].color.rgb; float3 lightColor = lights[i].color.rgb;
bool hasPos = lights[i].position.w == 1.0; if (lights[i].position.w == 0.0 && lights[i].direction.w == 0.0) {
bool hasDir = lights[i].direction.w == 1.0; diffuse += lightColor;
if (!hasPos && !hasDir) {
// D3DRMLIGHT_AMBIENT
result += input.Color.rgb * lightColor;
continue; continue;
} }
if (hasPos) { float3 lightVec;
// D3DRMLIGHT_POINT if (lights[i].direction.w == 1.0) {
lightVec = normalize(-lights[i].direction.xyz);
}
else {
float3 lightPos = lights[i].position.xyz; float3 lightPos = lights[i].position.xyz;
float3 lightDir = normalize(lightPos - fragPos); lightVec = lightPos - input.WorldPosition;
float diff = max(dot(normal, lightDir), 0.0);
float distance = length(lightPos - fragPos); float len = length(lightVec);
float attenuation = 1.0 / (1.0 + 0.09 * distance + 0.032 * distance * distance); if (len == 0.0f) {
float3 halfwayDir = normalize(lightDir + viewDir); continue;
float spec = pow(max(dot(normal, halfwayDir), 0.0), shininess); }
result += (input.Color.rgb * diff + spec) * lightColor * attenuation;
continue; lightVec /= len;
} }
if (hasDir) { float dotNL = dot(input.Normal, lightVec);
// D3DRMLIGHT_DIRECTIONAL if (dotNL > 0.0f) {
float3 lightDir = normalize(-lights[i].direction.xyz); diffuse += dotNL * lightColor;
float diff = max(dot(normal, lightDir), 0.0);
float3 halfwayDir = normalize(lightDir + viewDir); if (input.Shininess != 0.0f) {
float spec = pow(max(dot(normal, halfwayDir), 0.0), shininess); // Using dotNL ignores view angle, but this matches DirectX 5 behavior.
result += (input.Color.rgb * diff + spec) * lightColor; float spec1 = pow(dotNL, input.Shininess);
continue; specular += spec1 * lightColor;
}
} }
} }
output.Color = float4(result, input.Color.a); float3 baseColor = input.Color.rgb;
float3 finalColor = saturate(diffuse * baseColor + specular);
output.Color = float4(finalColor, input.Color.a);
output.Depth = input.Position.w; output.Depth = input.Position.w;
return output; return output;
} }

View File

@ -47,10 +47,10 @@ void Direct3DRMSoftwareRenderer::ClearZBuffer()
void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, D3DRMVECTOR4D& p) const void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, D3DRMVECTOR4D& p) const
{ {
float px = proj[0][0] * v.x + proj[1][0] * v.y + proj[2][0] * v.z + proj[3][0]; float px = proj[0][0] * v.position.x + proj[1][0] * v.position.y + proj[2][0] * v.position.z + proj[3][0];
float py = proj[0][1] * v.x + proj[1][1] * v.y + proj[2][1] * v.z + proj[3][1]; float py = proj[0][1] * v.position.x + proj[1][1] * v.position.y + proj[2][1] * v.position.z + proj[3][1];
float pz = proj[0][2] * v.x + proj[1][2] * v.y + proj[2][2] * v.z + proj[3][2]; float pz = proj[0][2] * v.position.x + proj[1][2] * v.position.y + proj[2][2] * v.position.z + proj[3][2];
float pw = proj[0][3] * v.x + proj[1][3] * v.y + proj[2][3] * v.z + proj[3][3]; float pw = proj[0][3] * v.position.x + proj[1][3] * v.position.y + proj[2][3] * v.position.z + proj[3][3];
p.w = pw; p.w = pw;
@ -69,23 +69,23 @@ void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, D3D
PositionColorVertex SplitEdge(PositionColorVertex a, const PositionColorVertex& b, float plane) PositionColorVertex SplitEdge(PositionColorVertex a, const PositionColorVertex& b, float plane)
{ {
float t = (plane - a.z) / (b.z - a.z); float t = (plane - a.position.z) / (b.position.z - a.position.z);
a.x = a.x + t * (b.x - a.x); a.position.x = a.position.x + t * (b.position.x - a.position.x);
a.y = a.y + t * (b.y - a.y); a.position.y = a.position.y + t * (b.position.y - a.position.y);
a.z = plane; a.position.z = plane;
a.u = a.u + t * (b.u - a.u); a.texCoord.u = a.texCoord.u + t * (b.texCoord.u - a.texCoord.u);
a.v = a.v + t * (b.v - a.v); a.texCoord.v = a.texCoord.v + t * (b.texCoord.v - a.texCoord.v);
a.nx = a.nx + t * (b.nx - a.nx); a.normals.x = a.normals.x + t * (b.normals.x - a.normals.x);
a.ny = a.ny + t * (b.ny - a.ny); a.normals.y = a.normals.y + t * (b.normals.y - a.normals.y);
a.nz = a.nz + t * (b.nz - a.nz); a.normals.z = a.normals.z + t * (b.normals.z - a.normals.z);
float len = std::sqrt(a.nx * a.nx + a.ny * a.ny + a.nz * a.nz); float len = std::sqrt(a.normals.x * a.normals.x + a.normals.y * a.normals.y + a.normals.z * a.normals.z);
if (len > 0.0001f) { if (len > 0.0001f) {
a.nx /= len; a.normals.x /= len;
a.ny /= len; a.normals.y /= len;
a.nz /= len; a.normals.z /= len;
} }
return a; return a;
@ -97,9 +97,9 @@ void Direct3DRMSoftwareRenderer::DrawTriangleClipped(
const PositionColorVertex& v2 const PositionColorVertex& v2
) )
{ {
bool in0 = v0.z >= m_front; bool in0 = v0.position.z >= m_front;
bool in1 = v1.z >= m_front; bool in1 = v1.position.z >= m_front;
bool in2 = v2.z >= m_front; bool in2 = v2.position.z >= m_front;
int insideCount = in0 + in1 + in2; int insideCount = in0 + in1 + in2;
@ -168,11 +168,11 @@ SDL_Color Direct3DRMSoftwareRenderer::ApplyLighting(const PositionColorVertex& v
FColor diffuse = {0, 0, 0, 0}; FColor diffuse = {0, 0, 0, 0};
// Position and normal // Position and normal
D3DVECTOR position = {vertex.x, vertex.y, vertex.z}; D3DVECTOR position = vertex.position;
D3DVECTOR normal = {vertex.nx, vertex.ny, vertex.nz}; D3DVECTOR normal = vertex.normals;
float normLen = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); float normLen = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
if (normLen == 0.0f) { if (normLen == 0.0f) {
return {vertex.r, vertex.g, vertex.b, vertex.a}; return vertex.colors;
} }
normal.x /= normLen; normal.x /= normLen;
@ -225,10 +225,10 @@ SDL_Color Direct3DRMSoftwareRenderer::ApplyLighting(const PositionColorVertex& v
} }
return SDL_Color{ return SDL_Color{
static_cast<Uint8>(std::min(255.0f, diffuse.r * vertex.r + specular.r * 255.0f)), static_cast<Uint8>(std::min(255.0f, diffuse.r * vertex.colors.r + specular.r * 255.0f)),
static_cast<Uint8>(std::min(255.0f, diffuse.g * vertex.g + specular.g * 255.0f)), static_cast<Uint8>(std::min(255.0f, diffuse.g * vertex.colors.g + specular.g * 255.0f)),
static_cast<Uint8>(std::min(255.0f, diffuse.b * vertex.b + specular.b * 255.0f)), static_cast<Uint8>(std::min(255.0f, diffuse.b * vertex.colors.b + specular.b * 255.0f)),
vertex.a vertex.colors.a
}; };
} }
@ -324,7 +324,7 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected(
Uint8 b = static_cast<Uint8>(w0 * c0.b + w1 * c1.b + w2 * c2.b); Uint8 b = static_cast<Uint8>(w0 * c0.b + w1 * c1.b + w2 * c2.b);
Uint8* pixelAddr = pixels + y * pitch + x * m_bytesPerPixel; Uint8* pixelAddr = pixels + y * pitch + x * m_bytesPerPixel;
if (v0.a == 255) { if (v0.colors.a == 255) {
zref = z; zref = z;
if (texels) { if (texels) {
@ -334,8 +334,12 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected(
continue; continue;
} }
invW = 1.0 / invW; invW = 1.0 / invW;
float u = static_cast<float>(((w0 * v0.u / p0.w) + (w1 * v1.u / p1.w) + (w2 * v2.u / p2.w)) * invW); float u = static_cast<float>(
float v = static_cast<float>(((w0 * v0.v / p0.w) + (w1 * v1.v / p1.w) + (w2 * v2.v / p2.w)) * invW); ((w0 * v0.texCoord.u / p0.w) + (w1 * v1.texCoord.u / p1.w) + (w2 * v2.texCoord.u / p2.w)) * invW
);
float v = static_cast<float>(
((w0 * v0.texCoord.v / p0.w) + (w1 * v1.texCoord.v / p1.w) + (w2 * v2.texCoord.v / p2.w)) * invW
);
// Tile textures // Tile textures
u = u - std::floor(u); u = u - std::floor(u);
@ -363,7 +367,7 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected(
} }
else { else {
// Transparent alpha blending with vertex alpha // Transparent alpha blending with vertex alpha
BlendPixel(pixelAddr, r, g, b, v0.a); BlendPixel(pixelAddr, r, g, b, v0.colors.a);
} }
} }
} }
@ -422,6 +426,7 @@ Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture)
if (texRef.texture == nullptr) { if (texRef.texture == nullptr) {
texRef.texture = texture; texRef.texture = texture;
texRef.cached = convertedRender; texRef.cached = convertedRender;
texRef.version = texture->m_version;
AddTextureDestroyCallback(i, texture); AddTextureDestroyCallback(i, texture);
return i; return i;
} }

View File

@ -296,20 +296,17 @@ HRESULT Direct3DRMViewportImpl::CollectSceneData()
} }
PositionColorVertex vtx; PositionColorVertex vtx;
vtx.x = viewPos.x; vtx.position = viewPos;
vtx.y = viewPos.y; vtx.normals = viewNorm;
vtx.z = viewPos.z; vtx.colors = {
vtx.nx = viewNorm.x; static_cast<Uint8>((color >> 16) & 0xFF),
vtx.ny = viewNorm.y; static_cast<Uint8>((color >> 8) & 0xFF),
vtx.nz = viewNorm.z; static_cast<Uint8>((color >> 0) & 0xFF),
vtx.r = (color >> 16) & 0xFF; static_cast<Uint8>((color >> 24) & 0xFF)
vtx.g = (color >> 8) & 0xFF; };
vtx.b = (color >> 0) & 0xFF;
vtx.a = (color >> 24) & 0xFF;
vtx.shininess = shininess; vtx.shininess = shininess;
vtx.texId = texId; vtx.texId = texId;
vtx.u = dv.tu; vtx.texCoord = {dv.tu, dv.tv};
vtx.v = dv.tv;
verts.push_back(vtx); verts.push_back(vtx);
} }
} }

View File

@ -6,14 +6,19 @@
#define NO_TEXTURE_ID 0xffffffff #define NO_TEXTURE_ID 0xffffffff
typedef struct PositionColorVertex { struct TexCoord {
float x, y, z;
float nx, ny, nz;
Uint8 r, g, b, a;
Uint32 texId = NO_TEXTURE_ID;
float u, v; float u, v;
};
struct PositionColorVertex {
D3DVECTOR position;
D3DVECTOR normals;
SDL_Color colors;
Uint32 texId;
TexCoord texCoord;
float shininess; float shininess;
} PositionColorVertex; };
static_assert(sizeof(PositionColorVertex) == 44);
struct FColor { struct FColor {
float r, g, b, a; float r, g, b, a;
@ -26,6 +31,7 @@ struct SceneLight {
D3DVECTOR direction; D3DVECTOR direction;
float directional = 0.f; // direction is valid if 1.f float directional = 0.f; // direction is valid if 1.f
}; };
static_assert(sizeof(SceneLight) == 48);
class Direct3DRMRenderer : public IDirect3DDevice2 { class Direct3DRMRenderer : public IDirect3DDevice2 {
public: public: