diff options
| -rw-r--r-- | src/video_core/pica.h | 8 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 68 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 20 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 82 | 
4 files changed, 165 insertions, 13 deletions
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index aad9effdc..c63d87a36 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -659,6 +659,8 @@ struct Regs {      enum class LightingLutInput {          NH = 0, // Cosine of the angle between the normal and half-angle vectors +        VH = 1, // Cosine of the angle between the view and half-angle vectors +        NV = 2, // Cosine of the angle between the normal and the view vector          LN = 3, // Cosine of the angle between the light and the normal vectors      }; @@ -709,7 +711,11 @@ struct Regs {          LightColor global_ambient; // emission + (material.ambient * lighting.ambient)          INSERT_PADDING_WORDS(0x1);          BitField<0, 3, u32> src_num; // number of enabled lights - 1 -        INSERT_PADDING_WORDS(0x1); + +        union { +            BitField< 4, 4, u32> config; +            BitField<27, 1, u32> clamp_highlights; +        } light_env;          union {              // Each bit specifies whether distance attenuation should be applied for the diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 80693fa29..c6fb37c53 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -297,6 +297,58 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {          SyncCombinerColor();          break; +    // Fragment lighting specular 0 color +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10): +        SyncLightSpecular0(0); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_0, 0x140 + 1 * 0x10): +        SyncLightSpecular0(1); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_0, 0x140 + 2 * 0x10): +        SyncLightSpecular0(2); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_0, 0x140 + 3 * 0x10): +        SyncLightSpecular0(3); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_0, 0x140 + 4 * 0x10): +        SyncLightSpecular0(4); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_0, 0x140 + 5 * 0x10): +        SyncLightSpecular0(5); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_0, 0x140 + 6 * 0x10): +        SyncLightSpecular0(6); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_0, 0x140 + 7 * 0x10): +        SyncLightSpecular0(7); +        break; + +    // Fragment lighting specular 1 color +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_1, 0x141 + 0 * 0x10): +        SyncLightSpecular1(0); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_1, 0x141 + 1 * 0x10): +        SyncLightSpecular1(1); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_1, 0x141 + 2 * 0x10): +        SyncLightSpecular1(2); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_1, 0x141 + 3 * 0x10): +        SyncLightSpecular1(3); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_1, 0x141 + 4 * 0x10): +        SyncLightSpecular1(4); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_1, 0x141 + 5 * 0x10): +        SyncLightSpecular1(5); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_1, 0x141 + 6 * 0x10): +        SyncLightSpecular1(6); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_1, 0x141 + 7 * 0x10): +        SyncLightSpecular1(7); +        break; +      // Fragment lighting diffuse color      case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10):          SyncLightDiffuse(0); @@ -835,6 +887,22 @@ void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {      }  } +void RasterizerOpenGL::SyncLightSpecular0(int light_index) { +    auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_0); +    if (color != uniform_block_data.data.light_src[light_index].specular_0) { +        uniform_block_data.data.light_src[light_index].specular_0 = color; +        uniform_block_data.dirty = true; +    } +} + +void RasterizerOpenGL::SyncLightSpecular1(int light_index) { +    auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_1); +    if (color != uniform_block_data.data.light_src[light_index].specular_1) { +        uniform_block_data.data.light_src[light_index].specular_1 = color; +        uniform_block_data.dirty = true; +    } +} +  void RasterizerOpenGL::SyncLightDiffuse(int light_index) {      auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse);      if (color != uniform_block_data.data.light_src[light_index].diffuse) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index ba0b05802..9e93b8b2f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -87,6 +87,10 @@ struct PicaShaderConfig {              res.light_src[light_index].dist_atten_scale = Pica::float20::FromRawFloat20(light.dist_atten_scale).ToFloat32();          } +        res.lighting_lut.d0_abs = (regs.lighting.abs_lut_input.d0 == 0); +        res.lighting_lut.d0_type = (Pica::Regs::LightingLutInput)regs.lighting.lut_input.d0.Value(); +        res.clamp_highlights = regs.lighting.light_env.clamp_highlights; +          return res;      } @@ -118,6 +122,12 @@ struct PicaShaderConfig {          bool lighting_enabled = false;          unsigned num_lights = 0; +        bool clamp_highlights = false; + +        struct { +            bool d0_abs = false; +            Pica::Regs::LightingLutInput d0_type = Pica::Regs::LightingLutInput::NH; +        } lighting_lut;      };  }; @@ -231,6 +241,10 @@ private:      };      struct LightSrc { +        std::array<GLfloat, 3> specular_0; +        INSERT_PADDING_WORDS(1); +        std::array<GLfloat, 3> specular_1; +        INSERT_PADDING_WORDS(1);          std::array<GLfloat, 3> diffuse;          INSERT_PADDING_WORDS(1);          std::array<GLfloat, 3> ambient; @@ -316,6 +330,12 @@ private:      /// Syncs the specified light's position to match the PICA register      void SyncLightPosition(int light_index); +    /// Syncs the specified light's specular 0 color to match the PICA register +    void SyncLightSpecular0(int light_index); + +    /// Syncs the specified light's specular 1 color to match the PICA register +    void SyncLightSpecular1(int light_index); +      /// Syncs the remaining OpenGL drawing state to match the current PICA state      void SyncDrawState(); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index cf99cff76..abcc89f1d 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -35,8 +35,7 @@ static void AppendSource(std::string& out, TevStageConfig::Source source,          out += "primary_fragment_color";          break;      case Source::SecondaryFragmentColor: -        // HACK: Until we implement fragment lighting, use zero -        out += "vec4(0.0)"; +        out += "secondary_fragment_color";          break;      case Source::Texture0:          out += "texture(tex[0], texcoord[0])"; @@ -334,6 +333,8 @@ in vec3 view;  out vec4 color;  struct LightSrc { +    vec3 specular_0; +    vec3 specular_1;      vec3 diffuse;      vec3 ambient;      vec3 position; @@ -358,6 +359,7 @@ uniform sampler2D tex[3];  void main() {  vec4 primary_fragment_color = vec4(0.0); +vec4 secondary_fragment_color = vec4(0.0);  )";      if (config.lighting_enabled) { @@ -367,41 +369,97 @@ vec4 primary_fragment_color = vec4(0.0);          out += "    1.f - 2.f*(normquat.x*normquat.x + normquat.y*normquat.y)));\n";          out += "vec4 secondary_color = vec4(0.0);\n";          out += "vec3 diffuse_sum = vec3(0.0);\n"; +        out += "vec3 specular_sum = vec3(0.0);\n";          out += "vec3 fragment_position = -view;\n"; +        out += "vec3 light_vector = vec3(0.0);\n"; +        out += "float dist_atten = 1.0;\n"; + +        // Gets the index into the specified lookup table for specular lighting +        auto GetLutIndex = [&](unsigned light_num, Regs::LightingLutInput input, bool abs) { +            const std::string half_angle = "normalize(view + light_vector)"; +            std::string index; +            switch (input) { +            case Regs::LightingLutInput::NH: +                index  = "dot(normal, " + half_angle + ")"; +                break; + +            case Regs::LightingLutInput::VH: +                index = std::string("dot(view, " + half_angle + ")"); +                break; + +            case Regs::LightingLutInput::NV: +                index = std::string("dot(normal, view)"); +                break; + +            case Regs::LightingLutInput::LN: +                index  = std::string("dot(light_vector, normal)"); +                break; + +            default: +                LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); +                UNIMPLEMENTED(); +                break; +            } + +            if (abs) { +                // In the range of [ 0.f, 1.f] +                index = config.light_src[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)"; +                return "clamp(int(" + index + " * 256.0), 0, 255)"; +            } else { +                // In the range of [-1.f, 1.f] +                index = "clamp(" + index + ", -1.0, 1.0)"; +                return std::string("uint(int(" + index + " * 127.f) & 0xff)"); +            } + +            return std::string(); +        };          for (unsigned light_index = 0; light_index < config.num_lights; ++light_index) {              unsigned num = config.light_src[light_index].num;              std::string light_src = "light_src[" + std::to_string(num) + "]"; -            std::string light_vector;              if (config.light_src[light_index].directional) -                light_vector = "normalize(-" + light_src + ".position)"; +                out += "light_vector = normalize(-" + light_src + ".position);\n";              else -                light_vector = "normalize(" + light_src + ".position - fragment_position)"; +                out += "light_vector = normalize(" + light_src + ".position - fragment_position);\n";              std::string dot_product;              if (config.light_src[light_index].two_sided_diffuse) -                dot_product = "abs(dot(" + light_vector + ", normal))"; +                dot_product = "abs(dot(light_vector, normal))";              else -                dot_product = "max(dot(" + light_vector + ", normal), 0.0)"; +                dot_product = "max(dot(light_vector, normal), 0.0)"; -            std::string dist_atten = "1.0"; +            // Compute distance attenuation value +            out += "dist_atten = 1.0;\n";              if (config.light_src[light_index].dist_atten_enabled) {                  std::string scale = std::to_string(config.light_src[light_index].dist_atten_scale);                  std::string bias = std::to_string(config.light_src[light_index].dist_atten_bias);                  std::string lut_index = "(" + scale + " * length(fragment_position - " + light_src + ".position) + " + bias + ")";                  std::string clamped_lut_index = "((clamp(int(" + lut_index + " * 256.0), 0, 255)))"; -                unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + num); - -                dist_atten = "lighting_lut_" + std::to_string(lut_num /4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "]"; +                const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + num); +                out += "dist_atten = lighting_lut_" + std::to_string(lut_num / 4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "];\n";              } -            out += "diffuse_sum += ((light_src[" + std::to_string(num) + "].diffuse * " + dot_product + ") + light_src[" + std::to_string(num) + "].ambient) * " + dist_atten + ";\n"; +            // Compute primary fragment color (diffuse lighting) function +            out += "diffuse_sum += ((light_src[" + std::to_string(num) + "].diffuse * " + dot_product + ") + light_src[" + std::to_string(num) + "].ambient) * dist_atten;\n"; + +            // Compute secondary fragment color (specular lighting) function +            std::string clamped_lut_index = GetLutIndex(num, config.lighting_lut.d0_type, config.lighting_lut.d0_abs); +            const unsigned lut_num = (unsigned)Regs::LightingSampler::Distribution0; +            std::string lut_lookup = "lighting_lut_" + std::to_string(lut_num / 4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "]"; + +            out += "specular_sum += (" + lut_lookup + " * light_src[" + std::to_string(num) + "].specular_0 * dist_atten);\n"; +        } + +        out += "float clamp_highlights = 1.0;\n"; +        if (config.clamp_highlights) { +            out += "if (dot(light_vector, normal) <= 0.0) clamp_highlights = 0.0;\n";          }          out += "diffuse_sum += lighting_global_ambient;\n";          out += "primary_fragment_color = vec4(clamp(diffuse_sum, vec3(0.0), vec3(1.0)), 1.0);\n"; +        out += "secondary_fragment_color = vec4(clamp(clamp_highlights * specular_sum, vec3(0.0), vec3(1.0)), 1.0);\n";      }      // Do not do any sort of processing if it's obvious we're not going to pass the alpha test  | 
