Skip to content

Commit

Permalink
Fog in ubershader
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Jul 21, 2024
1 parent a6e3d41 commit 4176a19
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 20 deletions.
2 changes: 1 addition & 1 deletion docs/3ds/lighting.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ lut_id is one of these values
6 RR

lut_index on the other hand represents the actual index of the LUT in the texture
u_tex_lighting_lut has 24 LUTs and they are used like so:
u_tex_luts has 24 LUTs for lighting and they are used like so:
0 D0
1 D1
2 is missing because SP uses LUTs 8-15
Expand Down
3 changes: 3 additions & 0 deletions include/PICA/gpu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class GPU {
// Set to false by the renderer when the lighting_lut is uploaded ot the GPU
bool lightingLUTDirty = false;

std::array<uint32_t, 128> fogLUT;
bool fogLUTDirty = false;

GPU(Memory& mem, EmulatorConfig& config);
void display() { renderer->display(); }
void screenshot(const std::string& name) { renderer->screenshot(name); }
Expand Down
12 changes: 12 additions & 0 deletions include/PICA/regs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ namespace PICA {
#undef defineTexEnv
// clang-format on

// Fog registers
FogColor = 0xE1,
FogLUTIndex = 0xE6,
FogLUTData0 = 0xE8,
FogLUTData1 = 0xE9,
FogLUTData2 = 0xEA,
FogLUTData3 = 0xEB,
FogLUTData4 = 0xEC,
FogLUTData5 = 0xED,
FogLUTData6 = 0xEE,
FogLUTData7 = 0xEF,

// Framebuffer registers
ColourOperation = 0x100,
BlendFunc = 0x101,
Expand Down
3 changes: 2 additions & 1 deletion include/renderer_gl/renderer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexBuffer dummyVBO;

OpenGL::Texture screenTexture;
OpenGL::Texture lightLUTTexture;
OpenGL::Texture LUTTexture;
OpenGL::Framebuffer screenFramebuffer;
OpenGL::Texture blankTexture;
// The "default" vertex shader to use when using specialized shaders but not PICA vertex shader -> GLSL recompilation
Expand All @@ -90,6 +90,7 @@ class RendererGL final : public Renderer {
void setupUbershaderTexEnv();
void bindTexturesToSlots();
void updateLightingLUT();
void updateFogLUT();
void initGraphicsContextInternal();

public:
Expand Down
3 changes: 3 additions & 0 deletions src/core/PICA/gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ void GPU::reset() {
lightingLUT.fill(0);
lightingLUTDirty = true;

fogLUT.fill(0);
fogLUTDirty = true;

totalAttribCount = 0;
fixedAttribMask = 0;
fixedAttribIndex = 0;
Expand Down
15 changes: 15 additions & 0 deletions src/core/PICA/regs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break;
}

case FogLUTData0:
case FogLUTData1:
case FogLUTData2:
case FogLUTData3:
case FogLUTData4:
case FogLUTData5:
case FogLUTData6:
case FogLUTData7: {
const uint32_t index = regs[FogLUTIndex] & 127;
fogLUT[index] = value;
fogLUTDirty = true;
regs[FogLUTIndex] = (index + 1) & 127;
break;
}

case LightingLUTData0:
case LightingLUTData1:
case LightingLUTData2:
Expand Down
4 changes: 2 additions & 2 deletions src/core/PICA/shader_gen_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
uniform sampler2D u_tex0;
uniform sampler2D u_tex1;
uniform sampler2D u_tex2;
uniform sampler2D u_tex_lighting_lut;
uniform sampler2D u_tex_luts;
)";

ret += uniformDefinition;
Expand All @@ -144,7 +144,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
}
float lutLookup(uint lut, int index) {
return texelFetch(u_tex_lighting_lut, ivec2(index, int(lut)), 0).r;
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
}
vec3 regToColor(uint reg) {
Expand Down
58 changes: 45 additions & 13 deletions src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,11 @@ void RendererGL::initGraphicsContextInternal() {
const u32 screenTextureWidth = 400; // Top screen is 400 pixels wide, bottom is 320
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall

lightLUTTexture.create(256, Lights::LUT_Count, GL_R32F);
lightLUTTexture.bind();
lightLUTTexture.setMinFilter(OpenGL::Linear);
lightLUTTexture.setMagFilter(OpenGL::Linear);
// 24 rows for light, 1 for fog
LUTTexture.create(256, Lights::LUT_Count + 1, GL_RG32F);
LUTTexture.bind();
LUTTexture.setMinFilter(OpenGL::Linear);
LUTTexture.setMagFilter(OpenGL::Linear);

auto prevTexture = OpenGL::getTex2D();

Expand Down Expand Up @@ -353,22 +354,49 @@ void RendererGL::bindTexturesToSlots() {
}

glActiveTexture(GL_TEXTURE0 + 3);
lightLUTTexture.bind();
LUTTexture.bind();
glActiveTexture(GL_TEXTURE0);
}

void RendererGL::updateLightingLUT() {
gpu.lightingLUTDirty = false;
std::array<float, GPU::LightingLutSize> lightingLut;
std::array<float, GPU::LightingLutSize * 2> lightingLut;

for (int i = 0; i < gpu.lightingLUT.size(); i++) {
uint64_t value = gpu.lightingLUT[i] & 0xFFF;
for (int i = 0; i < lightingLut.size(); i += 2) {
uint64_t value = gpu.lightingLUT[i >> 1] & 0xFFF;
lightingLut[i] = (float)(value << 4) / 65535.0f;
}

glActiveTexture(GL_TEXTURE0 + 3);
lightLUTTexture.bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, Lights::LUT_Count, GL_RED, GL_FLOAT, lightingLut.data());
LUTTexture.bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, Lights::LUT_Count, GL_RG, GL_FLOAT, lightingLut.data());
glActiveTexture(GL_TEXTURE0);
}

void RendererGL::updateFogLUT() {
gpu.fogLUTDirty = false;

// Fog LUT elements are of this type:
// 0-12 fixed1.1.11, Difference from next element
// 13-23 fixed0.0.11, Value
// We will store them as a 128x1 RG texture with R being the value and G being the difference
std::array<float, 128 * 2> fogLut;

for (int i = 0; i < fogLut.size(); i += 2) {
const uint32_t value = gpu.fogLUT[i >> 1];
int32_t diff = value & 0x1fff;
diff = (diff << 19) >> 19; // Sign extend the 13-bit value to 32 bits
const float fogDifference = float(diff) / 2048.0f;
const float fogValue = float((value >> 13) & 0x7ff) / 2048.0f;

fogLut[i] = fogValue;
fogLut[i + 1] = fogDifference;
}

glActiveTexture(GL_TEXTURE0 + 3);
LUTTexture.bind();
// The fog LUT exists at the end of the lighting LUT
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, Lights::LUT_Count, 128, 1, GL_RG, GL_FLOAT, fogLut.data());
glActiveTexture(GL_TEXTURE0);
}

Expand Down Expand Up @@ -453,6 +481,10 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v

bindTexturesToSlots();

if (gpu.fogLUTDirty) {
updateFogLUT();
}

if (gpu.lightingLUTDirty) {
updateLightingLUT();
}
Expand Down Expand Up @@ -811,7 +843,7 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);

// Allocate memory for the program UBO
glGenBuffers(1, &programEntry.uboBinding);
Expand Down Expand Up @@ -994,9 +1026,9 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
ubershaderData.depthmapEnableLoc = OpenGL::uniformLocation(program, "u_depthmapEnable");
ubershaderData.picaRegLoc = OpenGL::uniformLocation(program, "u_picaRegs");

// Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2, and the light maps go in TU 3
// Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2, light maps go in TU 3, and the fog map goes in TU 4
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
}
30 changes: 27 additions & 3 deletions src/host_shaders/opengl_fragment_shader.frag
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ uniform bool u_depthmapEnable;
uniform sampler2D u_tex0;
uniform sampler2D u_tex1;
uniform sampler2D u_tex2;
uniform sampler2D u_tex_lighting_lut;
uniform sampler2D u_tex_luts;

uniform uint u_picaRegs[0x200 - 0x48];

Expand Down Expand Up @@ -152,6 +152,8 @@ vec4 tevCalculateCombiner(int tev_id) {
#define RG_LUT 5u
#define RR_LUT 6u

#define FOG_INDEX 24

uint GPUREG_LIGHTi_CONFIG;
uint GPUREG_LIGHTING_CONFIG1;
uint GPUREG_LIGHTING_LUTINPUT_SELECT;
Expand All @@ -161,7 +163,7 @@ bool error_unimpl = false;
vec4 unimpl_color = vec4(1.0, 0.0, 1.0, 1.0);

float lutLookup(uint lut, int index) {
return texelFetch(u_tex_lighting_lut, ivec2(index, int(lut)), 0).r;
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
}

vec3 regToColor(uint reg) {
Expand Down Expand Up @@ -494,7 +496,7 @@ void main() {
if (tevUnimplementedSourceFlag) {
// fragColour = vec4(1.0, 0.0, 1.0, 1.0);
}
// fragColour.rg = texture(u_tex_lighting_lut,vec2(gl_FragCoord.x/200.,float(int(gl_FragCoord.y/2)%24))).rr;
// fragColour.rg = texture(u_tex_luts,vec2(gl_FragCoord.x/200.,float(int(gl_FragCoord.y/2)%24))).rr;

// Get original depth value by converting from [near, far] = [0, 1] to [-1, 1]
// We do this by converting to [0, 2] first and subtracting 1 to go to [-1, 1]
Expand All @@ -507,6 +509,28 @@ void main() {
// Write final fragment depth
gl_FragDepth = depth;

bool enable_fog = (textureEnvUpdateBuffer & 7u) == 5u;

if (enable_fog) {
bool flip_depth = (textureEnvUpdateBuffer & (1u << 16)) != 0u;
float fog_index = flip_depth ? 1.0 - depth : depth;
fog_index *= 128.0;
float clamped_index = clamp(floor(fog_index), 0.0, 127.0);
float delta = fog_index - clamped_index;
vec2 value = texelFetch(u_tex_luts, ivec2(int(clamped_index), FOG_INDEX), 0).rg;
float fog_factor = clamp(value.r + value.g * delta, 0.0, 1.0);

uint GPUREG_FOG_COLOR = readPicaReg(0x00E1u);

// Annoyingly color is not encoded in the same way as light color
float r = (GPUREG_FOG_COLOR & 0xFFu) / 255.0;
float g = ((GPUREG_FOG_COLOR >> 8) & 0xFFu) / 255.0;
float b = ((GPUREG_FOG_COLOR >> 16) & 0xFFu) / 255.0;
vec3 fog_color = vec3(r, g, b);

fragColour.rgb = mix(fog_color, fragColour.rgb, fog_factor);
}

// Perform alpha test
uint alphaControl = readPicaReg(0x104u);
if ((alphaControl & 1u) != 0u) { // Check if alpha test is on
Expand Down

0 comments on commit 4176a19

Please sign in to comment.