diff options
Diffstat (limited to 'src/video_core/renderer_opengl')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 69 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/pica_to_gl.h | 13 |
3 files changed, 71 insertions, 13 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index db53710aa..540cbb9d0 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -73,6 +73,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { state.lighting.light[light_index].num = num; state.lighting.light[light_index].directional = light.config.directional != 0; state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; + state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0; + state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0; state.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num); state.lighting.light[light_index].spot_atten_enable = @@ -518,14 +520,16 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" "vec3 light_vector = vec3(0.0);\n" "vec3 refl_value = vec3(0.0);\n" - "vec3 spot_dir = vec3(0.0);\n;"; + "vec3 spot_dir = vec3(0.0);\n" + "vec3 half_vector = vec3(0.0);\n" + "float geo_factor = 1.0;\n"; - // Compute fragment normals + // Compute fragment normals and tangents + const std::string pertubation = + "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { - // Bump mapping is enabled using a normal map, read perturbation vector from the selected - // texture - out += "vec3 surface_normal = 2.0 * (" + SampleTexture(config, lighting.bump_selector) + - ").rgb - 1.0;\n"; + // Bump mapping is enabled using a normal map + out += "vec3 surface_normal = " + pertubation + ";\n"; // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher // precision result @@ -534,31 +538,41 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; } + + // The tangent vector is not perturbed by the normal map and is just a unit vector. + out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { // Bump mapping is enabled using a tangent map - LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); - UNIMPLEMENTED(); + out += "vec3 surface_tangent = " + pertubation + ";\n"; + // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant + // computation below, which is also confirmed on 3DS. So we don't bother recomputing here + // even if 'renorm' is enabled. + + // The normal vector is not perturbed by the tangent map and is just a unit vector. + out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; } else { - // No bump mapping - surface local normal is just a unit normal + // No bump mapping - surface local normal and tangent are just unit vectors out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; + out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; } // Rotate the surface-local normal by the interpolated normal quaternion to convert it to // eyespace. - out += "vec3 normal = quaternion_rotate(normalize(normquat), surface_normal);\n"; + out += "vec4 normalized_normquat = normalize(normquat);\n"; + out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; + out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; // Gets the index into the specified lookup table for specular lighting auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input, bool abs) { - const std::string half_angle = "normalize(normalize(view) + light_vector)"; std::string index; switch (input) { case LightingRegs::LightingLutInput::NH: - index = "dot(normal, " + half_angle + ")"; + index = "dot(normal, normalize(half_vector))"; break; case LightingRegs::LightingLutInput::VH: - index = std::string("dot(normalize(view), " + half_angle + ")"); + index = std::string("dot(normalize(view), normalize(half_vector))"); break; case LightingRegs::LightingLutInput::NV: @@ -573,6 +587,22 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { index = std::string("dot(light_vector, spot_dir)"); break; + case LightingRegs::LightingLutInput::CP: + // CP input is only available with configuration 7 + if (lighting.config == LightingRegs::LightingConfig::Config7) { + // Note: even if the normal vector is modified by normal map, which is not the + // normal of the tangent plane anymore, the half angle vector is still projected + // using the modified normal vector. + std::string half_angle_proj = "normalize(half_vector) - normal / dot(normal, " + "normal) * dot(normal, normalize(half_vector))"; + // Note: the half angle vector projection is confirmed not normalized before the dot + // product. The result is in fact not cos(phi) as the name suggested. + index = "dot(" + half_angle_proj + ", tangent)"; + } else { + index = "0.0"; + } + break; + default: LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); UNIMPLEMENTED(); @@ -610,6 +640,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { out += "light_vector = normalize(" + light_src + ".position + view);\n"; out += "spot_dir = " + light_src + ".spot_direction;\n"; + out += "half_vector = normalize(view) + light_vector;\n"; // Compute dot product of light_vector and normal, adjust if lighting is one-sided or // two-sided @@ -643,6 +674,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { std::string clamp_highlights = lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; + if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { + out += "geo_factor = dot(half_vector, half_vector);\n" + "geo_factor = geo_factor == 0.0 ? 0.0 : min(" + + dot_product + " / geo_factor, 1.0);\n"; + } + // Specular 0 component std::string d0_lut_value = "1.0"; if (lighting.lut_d0.enable && @@ -655,6 +692,9 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")"; } std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; + if (light_config.geometric_factor_0) { + specular_0 = "(" + specular_0 + " * geo_factor)"; + } // If enabled, lookup ReflectRed value, otherwise, 1.0 is used if (lighting.lut_rr.enable && @@ -710,6 +750,9 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { } std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; + if (light_config.geometric_factor_1) { + specular_1 = "(" + specular_1 + " * geo_factor)"; + } // Fresnel if (lighting.lut_fr.enable && diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 9c90eadf9..2302ae453 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -94,6 +94,8 @@ union PicaShaderConfig { bool two_sided_diffuse; bool dist_atten_enable; bool spot_atten_enable; + bool geometric_factor_0; + bool geometric_factor_1; } light[8]; bool enable; diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 93d7b0b71..70298e211 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -55,6 +55,12 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder GL_REPEAT, // WrapMode::Repeat GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat + // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the + // comments in enum WrapMode. + GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2 + GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2 + GL_REPEAT, // WrapMode::Repeat2 + GL_REPEAT, // WrapMode::Repeat3 }; // Range check table for input @@ -65,6 +71,13 @@ inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { return GL_CLAMP_TO_EDGE; } + if (static_cast<u32>(mode) > 3) { + // It is still unclear whether mode 4-7 are valid, so log it if a game uses them. + // TODO(wwylele): telemetry should be added here so we can collect more info about which + // game uses this. + LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode)); + } + GLenum gl_mode = wrap_mode_table[mode]; // Check for dummy values indicating an unknown mode |
