Merge pull request #549 from OFFTKP/fog

Implement fog
This commit is contained in:
wheremyfoodat 2024-07-21 19:01:13 +00:00 committed by GitHub
commit cb2448a004
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 178 additions and 33 deletions

View file

@ -74,6 +74,9 @@ void GPU::reset() {
lightingLUT.fill(0);
lightingLUTDirty = true;
fogLUT.fill(0);
fogLUTDirty = true;
totalAttribCount = 0;
fixedAttribMask = 0;
fixedAttribIndex = 0;

View file

@ -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] & 0x7F;
fogLUT[index] = value;
fogLUTDirty = true;
regs[FogLUTIndex] = (index + 1) & 0x7F;
break;
}
case LightingLUTData0:
case LightingLUTData1:
case LightingLUTData2:

View file

@ -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;
@ -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) {
@ -194,6 +194,8 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
compileTEV(ret, i, config);
}
compileFog(ret, config);
applyAlphaTest(ret, config);
ret += "fragColor = combinerOutput;\n}"; // End of main function
@ -652,4 +654,27 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
}
}
}
void FragmentGenerator::compileFog(std::string& shader, const PICA::FragmentConfig& config) {
if (config.fogConfig.mode != FogMode::Fog) {
return;
}
float r = config.fogConfig.fogColorR / 255.0f;
float g = config.fogConfig.fogColorG / 255.0f;
float b = config.fogConfig.fogColorB / 255.0f;
if (config.fogConfig.flipDepth) {
shader += "float fog_index = (1.0 - depth) * 128.0;\n";
} else {
shader += "float fog_index = depth * 128.0;\n";
}
shader += "float clamped_index = clamp(floor(fog_index), 0.0, 127.0);";
shader += "float delta = fog_index - clamped_index;";
shader += "vec3 fog_color = vec3(" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + ");";
shader += "vec2 value = texelFetch(u_tex_luts, ivec2(int(clamped_index), 24), 0).rg;"; // fog LUT is past the light LUTs
shader += "float fog_factor = clamp(value.r + value.g * delta, 0.0, 1.0);";
shader += "combinerOutput.rgb = mix(fog_color, combinerOutput.rgb, fog_factor);";
}

View file

@ -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();
@ -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);
}
@ -453,6 +481,10 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
bindTexturesToSlots();
if (gpu.fogLUTDirty) {
updateFogLUT();
}
if (gpu.lightingLUTDirty) {
updateLightingLUT();
}
@ -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);
@ -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 and the LUTs go in TU 3
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);
}