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 "d3drmrenderer.h"
#include "d3drmrenderer_sdl3gpu.h"
#include "ddraw_impl.h"
#include "miniwin.h"
#include <SDL3/SDL.h>
#include <cstddef>
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].instance_step_rate = 0;
SDL_GPUVertexAttribute vertexAttrs[3] = {};
SDL_GPUVertexAttribute vertexAttrs[6] = {};
vertexAttrs[0].location = 0;
vertexAttrs[0].buffer_slot = 0;
vertexAttrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
vertexAttrs[0].offset = 0;
vertexAttrs[0].offset = offsetof(PositionColorVertex, position);
vertexAttrs[1].location = 1;
vertexAttrs[1].buffer_slot = 0;
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].buffer_slot = 0;
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 = {};
vertexInputState.vertex_buffer_descriptions = vertexBufferDescs;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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