Skip to content

Commit

Permalink
Light tuning / fixes
Browse files Browse the repository at this point in the history
- Implement software-side ambient for non-lightmap room vobs
- Implement sunlight flag to prevent worldmesh/VOBs from lightmap rooms from getting sunlight
- adjust all light strenghts
- debug shader modes repurposed from static vs.lightmap to sun vs. static(includes lightmap)
  • Loading branch information
Katharsas committed Apr 21, 2024
1 parent 2bc85c2 commit d4306a2
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 35 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -9,6 +9,10 @@ Simple, high performance DirectX11.1 renderer for Gothic assets (VDF or single f
### Currently supports
- Worldmesh (ZEN)
- Static Objects (VOBs)
- Static Light
- Lightmaps
- Per-Vertex Colors
- Per-Vob Static Light Accumulation (Color & Direction)

### Features
- Frame Limiter
Expand All @@ -27,8 +31,6 @@ Simple, high performance DirectX11.1 renderer for Gothic assets (VDF or single f
### TODO
- Sky
- Water / Morph-Meshes
- Per-Vertex baked shadows
- Lightmaps
- Time Of Day (Gothic LUT Tonemapping)

### Options
Expand Down
46 changes: 27 additions & 19 deletions ZenRen/Shaders/mainPass.hlsl
Expand Up @@ -31,6 +31,7 @@ struct VS_IN
float3 uvLightmap : TEXCOORD1;
float4 colLight : COLOR;
float3 dirLight : NORMAL1;
float sunLight : TEXCOORD2;
};

struct VS_OUT
Expand Down Expand Up @@ -66,23 +67,20 @@ float CalcLightDirectional(float3 dirLight, float3 dirNormal, float lightRatioAt

float CalcLightSun(float3 dirLight, float3 dirNormal)
{
float lightReceived = CalcLightDirectional(dirLight, dirNormal, 0.20f, 0.10f);
float strength = 2.f;
float lightReceived = CalcLightDirectional(dirLight, dirNormal, 0.6f, 0.3f);
//float strength = 2.f;
float strength = 1.0f;
return lightReceived * strength;
}

float CalcLightStatic(float3 dirLight, float3 dirNormal)
float CalcLightStaticVob(float3 dirLight, float3 dirNormal)
{
float lightReceived = 1;
if (dirLight.x > -99) {
lightReceived = CalcLightDirectional(dirLight, dirNormal, 0.20f, 0.07f);
}
float strength = 2.1f;
float lightReceived = CalcLightDirectional(dirLight, dirNormal, 0.f, 0.f);
//float strength = 2.1f;
float strength = 5.5f;
return lightReceived * strength;
}

static float staticLightOnlyFactor = 1.2f;

VS_OUT VS_Main(VS_IN input)
{
VS_OUT output;
Expand All @@ -102,11 +100,10 @@ VS_OUT VS_Main(VS_IN input)

bool enableLightSun = false;
bool enableLightStatic = false;
bool isVob = (input.dirLight.x > -99);

if (!debugOutput || debugOutputType == FLAG_OUTPUT_SOLID) {
if (input.dirLight.x <= -99) {
enableLightSun = true;
}
enableLightSun = true;
enableLightStatic = true;
}
if (debugOutput && debugOutputType == FLAG_OUTPUT_LIGHT_SUN) {
Expand All @@ -116,21 +113,33 @@ VS_OUT VS_Main(VS_IN input)
enableLightStatic = true;
}

float3 lightSun = (float3) (input.sunLight * CalcLightSun(viewLight3, viewNormal3));
float3 lightStatic;
float3 lightAmbient;
if (isVob) {
lightStatic = input.colLight * CalcLightStaticVob(input.dirLight, viewNormal3);
lightAmbient = input.colLight * 0.16f;// original SRGB factor = 0.4f
}
else {
lightStatic = input.colLight;
lightAmbient = 0.f;
}

output.light = (float3) 0;
float3 lightSun = (float3) (CalcLightSun(viewLight3, viewNormal3));
float3 lightStatic = input.colLight * CalcLightStatic(input.dirLight, viewNormal3);
if (enableLightSun && enableLightStatic) {
output.light = (lightSun * 0.5f) + (lightStatic * 0.5f);
output.light = (lightSun * 0.3f) + ((lightAmbient + lightStatic) * 0.7f);
}
else if (enableLightSun) {
output.light = lightSun;
output.light = lightSun * 0.3f;
}
else if (enableLightStatic) {
output.light = lightStatic * staticLightOnlyFactor;
output.light = (lightAmbient + lightStatic) * 0.7f;
}
else {
output.light = (float3) 1;
}

output.light *= 1.1;// TODO do this in tonemapping stage or something

output.position = mul(viewPosition, projectionMatrix);
output.uvBaseColor = input.uvBaseColor;
Expand Down Expand Up @@ -224,7 +233,6 @@ float4 PS_Main(PS_IN input) : SV_TARGET
|| debugOutputType == FLAG_OUTPUT_LIGHT_STATIC) {
if (input.uvLightmap.z >= 0) /* dynamic branch */ {
lightColor = SampleLightmap(input.uvLightmap);
lightColor.rgb *= staticLightOnlyFactor;
}
}

Expand Down
10 changes: 5 additions & 5 deletions ZenRen/src/renderer/PipelineWorld.cpp
Expand Up @@ -26,8 +26,8 @@ namespace renderer::world {
Solid = 0,
Diffuse = 1,
Normal = 2,
Light_Static = 3,
Lightmap = 4,
Light_Sun = 3,
Light_Static = 4,
};

__declspec(align(16))
Expand Down Expand Up @@ -299,12 +299,12 @@ namespace renderer::world {
else if (settings.shader.mode == ShaderMode::Normals) {
cbGlobalSettings.outputDirectType = ShaderOutputDirect::Normal;
}
else if (settings.shader.mode == ShaderMode::Light_Sun) {
cbGlobalSettings.outputDirectType = ShaderOutputDirect::Light_Sun;
}
else if (settings.shader.mode == ShaderMode::Light_Static) {
cbGlobalSettings.outputDirectType = ShaderOutputDirect::Light_Static;
}
else if (settings.shader.mode == ShaderMode::Lightmap) {
cbGlobalSettings.outputDirectType = ShaderOutputDirect::Lightmap;
}
else if (settings.shader.mode == ShaderMode::Solid || settings.shader.mode == ShaderMode::Default) {
cbGlobalSettings.outputDirectType = ShaderOutputDirect::Solid;
}
Expand Down
2 changes: 1 addition & 1 deletion ZenRen/src/renderer/RenderSettings.cpp
Expand Up @@ -7,7 +7,7 @@
namespace renderer::gui::settings {

// TODO we should have checkbox toggles for each light type so we can add them up however we want
const std::array<std::string, 6> shaderModeItems = { "Full", "Solid Only", "Diffuse Only", "Normals Only", "Light Static", "Lightmaps Only" };
const std::array<std::string, 6> shaderModeItems = { "Full", "Solid Only", "Diffuse Only", "Normals Only", "Light Sun", "Light Static" };
std::string shaderModeSelected = shaderModeItems[0];
const std::array<std::string, 5> filterSettingsItems = { "Trilinear", "AF x2", "AF x4", "AF x8", "AF x16" };
std::string filterSettingsSelected = filterSettingsItems[4];
Expand Down
5 changes: 3 additions & 2 deletions ZenRen/src/renderer/Renderer.h
Expand Up @@ -12,8 +12,8 @@ namespace renderer
Solid,
Diffuse,
Normals,
Light_Static,
Lightmap
Light_Sun,
Light_Static
};

struct ShaderSettings {
Expand Down Expand Up @@ -46,6 +46,7 @@ namespace renderer
std::string meshName;
DirectX::XMMATRIX transform;
std::array<VEC3, 2> bbox;// pos_min, pos_max
bool receiveLightSun;
D3DXCOLOR colLightStatic;
DirectX::XMVECTOR dirLightStatic;// pre-inverted
};
Expand Down
1 change: 1 addition & 0 deletions ZenRen/src/renderer/RendererCommon.h
Expand Up @@ -76,6 +76,7 @@ namespace renderer
ARRAY_UV uvLightmap;
D3DXCOLOR colLight;
VEC3 dirLight;
float lightSun;
};
inline std::ostream& operator <<(std::ostream& os, const NORMAL_CL_UV_LUV_STATIC_LIGHT& that)
{
Expand Down
1 change: 1 addition & 0 deletions ZenRen/src/renderer/ShaderManager.cpp
Expand Up @@ -54,6 +54,7 @@ namespace renderer {
//{ "INDEX_LIGHTMAP", 0, DXGI_FORMAT_R16_SINT, 1 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1 },
{ "NORMAL", 1, DXGI_FORMAT_R32G32B32_FLOAT, 1 },
{ "TEXCOORD", 2, DXGI_FORMAT_R32_FLOAT, 1 },
};
shaders[shaderName] = new Shader(d3d, filePath(shaderName), layoutDesc);
} {
Expand Down
2 changes: 2 additions & 0 deletions ZenRen/src/renderer/loader/MeshFromVdfLoader.cpp
Expand Up @@ -110,6 +110,7 @@ namespace renderer::loader {
other.colLight = fromSRGB(D3DXCOLOR(zenVert.Color));
//other.colLight = D3DXCOLOR(1, 1, 1, 0.f);
other.dirLight = { -100, -100, -100 };// value that is easy to check as not normalized in shader
other.lightSun = faceLightmapIndex == -1 ? 1 : 0;

if (faceLightmapIndex == -1) {
other.uvLightmap = { 0, 0, -1 };
Expand Down Expand Up @@ -238,6 +239,7 @@ namespace renderer::loader {
other.uvLightmap = { 0, 0, -1 };
other.colLight = instance.colLightStatic;
other.dirLight = toVec3(XMVector3Normalize(instance.dirLightStatic));
other.lightSun = instance.receiveLightSun ? 1 : 0;

// flip faces (seems like zEngine uses counter-clockwise winding, while we use clockwise winding)
// TODO use D3D11_RASTERIZER_DESC FrontCounterClockwise instead?
Expand Down
6 changes: 5 additions & 1 deletion ZenRen/src/renderer/loader/MeshUtil.cpp
Expand Up @@ -51,7 +51,7 @@ namespace renderer::loader
}
}

inline float fromSRGB(const float channel) {
float fromSRGB(const float channel) {
return (channel <= 0.04045f) ? (channel / 12.92f) : pow((channel + 0.055f) / 1.055f, 2.4f);
}

Expand All @@ -63,4 +63,8 @@ namespace renderer::loader
result.b = fromSRGB(result.b);
return result;
}

D3DXCOLOR greyscale(const float channel) {
return D3DXCOLOR(channel, channel, channel, 1);
}
}
3 changes: 2 additions & 1 deletion ZenRen/src/renderer/loader/MeshUtil.h
Expand Up @@ -49,6 +49,7 @@ namespace renderer::loader

void warnIfNotNormalized(const DirectX::XMVECTOR& source);

inline float fromSRGB(const float channel);
float fromSRGB(const float channel);
D3DXCOLOR fromSRGB(const D3DXCOLOR color);
D3DXCOLOR greyscale(const float channel);
}
7 changes: 4 additions & 3 deletions ZenRen/src/renderer/loader/StaticLightFromGroundFace.cpp
Expand Up @@ -133,10 +133,11 @@ namespace renderer::loader
float v0Contrib = 1 - (v0Distance / totalDistance);
float v1Contrib = 1 - (v1Distance / totalDistance);
float v2Contrib = 1 - (v2Distance / totalDistance);
D3DXCOLOR v0Color = others[vertIndex].colLight;
D3DXCOLOR v1Color = others[vertIndex + 1].colLight;
D3DXCOLOR v2Color = others[vertIndex + 2].colLight;
D3DXCOLOR colorAverage =
((others[vertIndex].colLight * v0Contrib)
+ (others[vertIndex + 1].colLight * v1Contrib)
+ (others[vertIndex + 2].colLight * v2Contrib)) / 2.f;
((v0Color * v0Contrib) + (v1Color * v1Contrib) + (v2Color * v2Contrib)) / 2.f;
return colorAverage;
}

Expand Down
21 changes: 20 additions & 1 deletion ZenRen/src/renderer/loader/ZenLoader.cpp
Expand Up @@ -305,6 +305,7 @@ namespace renderer::loader {
hasLightmap = true;
}
}
instance.receiveLightSun = !hasLightmap;

if (hasLightmap) {
// TODO
Expand All @@ -317,7 +318,7 @@ namespace renderer::loader {
if (optLight.has_value()) {
colLight = optLight.value().color;
//multiplyColor(light.color, fromSRGB(0.71f));
//multiplyColor(colLight, fromSRGB(0.88f));
multiplyColor(colLight, fromSRGB(0.85f));

instance.dirLightStatic = optLight.value().dirInverted;
}
Expand All @@ -339,9 +340,27 @@ namespace renderer::loader {
}
}

// outdoor vobs get additional fixed ambient
if (isOutdoorLevel && !hasLightmap) {
// TODO
// Original game knows if a ground poly is in a portal room (without lightmap, like a cave) or actually outdoors
// from per-ground-face flag. Find out how this is calculated or shoot some rays towards the sky.
bool isVobIndoor = false;
if (isVobIndoor) {
colLight = colLight * fromSRGB(0.8f) + fromSRGB(greyscale(0.2f));
colLight = (colLight * 1.4f) + greyscale(0.05f);// simulate effect of SRGB addition error
}
else {
colLight = colLight * fromSRGB(0.5f) + fromSRGB(greyscale(0.5f));
colLight = (colLight * 2.5f) - greyscale(0.25f);// simulate effect of SRGB addition error
}
}

if (debugTintVobStaticLight) {
colLight = D3DXCOLOR((colLight.r / 3.f) * 2.f, colLight.g, colLight.b, colLight.a);
}

colLight.a = 1;
instance.colLightStatic = colLight;

const auto duration = std::chrono::high_resolution_clock::now() - now;
Expand Down

0 comments on commit d4306a2

Please sign in to comment.