diff options
| author | Sebastian Valle <subv2112@gmail.com> | 2017-06-11 18:23:47 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-06-11 18:23:47 +0000 | 
| commit | 39c7c1f580a1c40a12e65b6c9c65215826e73efb (patch) | |
| tree | 75a2895b108e5460fee8695ebe5b91dbf59e9af2 | |
| parent | 9a8a90b52b4a7254ad73afbd23605516c50c5417 (diff) | |
| parent | 10906dceec42b1975a7f10abd199678dce692838 (diff) | |
Merge pull request #2727 from wwylele/spot-light
Fragment lighting: implement spot light 
| -rw-r--r-- | src/video_core/regs_lighting.h | 63 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 45 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 6 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 39 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 3 | 
5 files changed, 128 insertions, 28 deletions
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h index 6793405d9..fbfebc0a7 100644 --- a/src/video_core/regs_lighting.h +++ b/src/video_core/regs_lighting.h @@ -26,6 +26,16 @@ struct LightingRegs {          DistanceAttenuation = 16,      }; +    static LightingSampler SpotlightAttenuationSampler(unsigned index) { +        return static_cast<LightingSampler>( +            static_cast<unsigned>(LightingSampler::SpotlightAttenuation) + index); +    } + +    static LightingSampler DistanceAttenuationSampler(unsigned index) { +        return static_cast<LightingSampler>( +            static_cast<unsigned>(LightingSampler::DistanceAttenuation) + index); +    } +      /**      * Pica fragment lighting supports using different LUTs for each lighting component:  Reflectance      * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor, @@ -73,6 +83,8 @@ struct LightingRegs {          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 +        SP = 4, // Cosine of the angle between the light and the inverse spotlight vectors +        CP = 5, // TODO: document and implement      };      enum class LightingBumpMode : u32 { @@ -104,6 +116,9 @@ struct LightingRegs {              return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&                     (config != LightingConfig::Config5); +        case LightingSampler::SpotlightAttenuation: +            return (config != LightingConfig::Config2) && (config != LightingConfig::Config3); +          case LightingSampler::Fresnel:              return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&                     (config != LightingConfig::Config4); @@ -116,11 +131,10 @@ struct LightingRegs {              return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||                     (config == LightingConfig::Config7);          default: -            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached " -                            "unreachable section, sampler should be one " -                            "of Distribution0, Distribution1, Fresnel, " -                            "ReflectRed, ReflectGreen or ReflectBlue, instead " -                            "got %i", +            UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached unreachable section, " +                            "sampler should be one of Distribution0, Distribution1, " +                            "SpotlightAttenuation, Fresnel, ReflectRed, ReflectGreen or " +                            "ReflectBlue, instead got %i",                              static_cast<int>(config));          }      } @@ -140,7 +154,16 @@ struct LightingRegs {              BitField<0, 16, u32> z;          }; -        INSERT_PADDING_WORDS(0x3); +        // inverse spotlight direction vector, encoded as fixed1.1.11 +        union { +            BitField<0, 13, s32> spot_x; +            BitField<16, 13, s32> spot_y; +        }; +        union { +            BitField<0, 13, s32> spot_z; +        }; + +        INSERT_PADDING_WORDS(0x1);          union {              BitField<0, 1, u32> directional; @@ -169,8 +192,16 @@ struct LightingRegs {      } config0;      union { +        u32 raw; + +        // Each bit specifies whether spot light attenuation should be applied for the corresponding +        // light. +        BitField<8, 8, u32> disable_spot_atten; +          BitField<16, 1, u32> disable_lut_d0;          BitField<17, 1, u32> disable_lut_d1; +        // Note: by intuition, BitField<18, 1, u32> should be disable_lut_sp, but it is actually a +        // dummy bit which is always set as 1.          BitField<19, 1, u32> disable_lut_fr;          BitField<20, 1, u32> disable_lut_rr;          BitField<21, 1, u32> disable_lut_rg; @@ -178,23 +209,15 @@ struct LightingRegs {          // Each bit specifies whether distance attenuation should be applied for the corresponding          // light. -        BitField<24, 1, u32> disable_dist_atten_light_0; -        BitField<25, 1, u32> disable_dist_atten_light_1; -        BitField<26, 1, u32> disable_dist_atten_light_2; -        BitField<27, 1, u32> disable_dist_atten_light_3; -        BitField<28, 1, u32> disable_dist_atten_light_4; -        BitField<29, 1, u32> disable_dist_atten_light_5; -        BitField<30, 1, u32> disable_dist_atten_light_6; -        BitField<31, 1, u32> disable_dist_atten_light_7; +        BitField<24, 8, u32> disable_dist_atten;      } config1;      bool IsDistAttenDisabled(unsigned index) const { -        const unsigned disable[] = { -            config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1, -            config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3, -            config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5, -            config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7}; -        return disable[index] != 0; +        return (config1.disable_dist_atten & (1 << index)) != 0; +    } + +    bool IsSpotAttenDisabled(unsigned index) const { +        return (config1.disable_spot_atten & (1 << index)) != 0;      }      union { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index aa9b831dd..57d5e8253 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -735,6 +735,40 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {          SyncLightPosition(7);          break; +    // Fragment spot lighting direction +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_x, 0x146 + 0 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_z, 0x147 + 0 * 0x10): +        SyncLightSpotDirection(0); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_x, 0x146 + 1 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_z, 0x147 + 1 * 0x10): +        SyncLightSpotDirection(1); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_x, 0x146 + 2 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_z, 0x147 + 2 * 0x10): +        SyncLightSpotDirection(2); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_x, 0x146 + 3 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_z, 0x147 + 3 * 0x10): +        SyncLightSpotDirection(3); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_x, 0x146 + 4 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_z, 0x147 + 4 * 0x10): +        SyncLightSpotDirection(4); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_x, 0x146 + 5 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_z, 0x147 + 5 * 0x10): +        SyncLightSpotDirection(5); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_x, 0x146 + 6 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_z, 0x147 + 6 * 0x10): +        SyncLightSpotDirection(6); +        break; +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_x, 0x146 + 7 * 0x10): +    case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_z, 0x147 + 7 * 0x10): +        SyncLightSpotDirection(7); +        break; +      // Fragment lighting light source config      case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10):      case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): @@ -1595,6 +1629,17 @@ void RasterizerOpenGL::SyncLightPosition(int light_index) {      }  } +void RasterizerOpenGL::SyncLightSpotDirection(int light_index) { +    const auto& light = Pica::g_state.regs.lighting.light[light_index]; +    GLvec3 spot_direction = {light.spot_x / 2047.0f, light.spot_y / 2047.0f, +                             light.spot_z / 2047.0f}; + +    if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { +        uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; +        uniform_block_data.dirty = true; +    } +} +  void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) {      GLfloat dist_atten_bias =          Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index a9ad7d660..d9a3e9d1c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -125,6 +125,7 @@ private:          alignas(16) GLvec3 diffuse;          alignas(16) GLvec3 ambient;          alignas(16) GLvec3 position; +        alignas(16) GLvec3 spot_direction; // negated          GLfloat dist_atten_bias;          GLfloat dist_atten_scale;      }; @@ -153,7 +154,7 @@ private:      };      static_assert( -        sizeof(UniformData) == 0x3E0, +        sizeof(UniformData) == 0x460,          "The size of the UniformData structure has changed, update the structure in the shader");      static_assert(sizeof(UniformData) < 16384,                    "UniformData structure must be less than 16kb as per the OpenGL spec"); @@ -241,6 +242,9 @@ private:      /// Syncs the specified light's position to match the PICA register      void SyncLightPosition(int light_index); +    /// Syncs the specified spot light direcition to match the PICA register +    void SyncLightSpotDirection(int light_index); +      /// Syncs the specified light's distance attenuation bias to match the PICA register      void SyncLightDistanceAttenuationBias(int light_index); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index ffe419863..db53710aa 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -75,6 +75,8 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {          state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0;          state.lighting.light[light_index].dist_atten_enable =              !regs.lighting.IsDistAttenDisabled(num); +        state.lighting.light[light_index].spot_atten_enable = +            !regs.lighting.IsSpotAttenDisabled(num);      }      state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; @@ -87,6 +89,12 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {      state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();      state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); +    // this is a dummy field due to lack of the corresponding register +    state.lighting.lut_sp.enable = true; +    state.lighting.lut_sp.abs_input = regs.lighting.abs_lut_input.disable_sp == 0; +    state.lighting.lut_sp.type = regs.lighting.lut_input.sp.Value(); +    state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp); +      state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0;      state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;      state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); @@ -509,7 +517,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {      out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"             "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 refl_value = vec3(0.0);\n" +           "vec3 spot_dir = vec3(0.0);\n;";      // Compute fragment normals      if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { @@ -560,6 +569,10 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {              index = std::string("dot(light_vector, normal)");              break; +        case LightingRegs::LightingLutInput::SP: +            index = std::string("dot(light_vector, spot_dir)"); +            break; +          default:              LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);              UNIMPLEMENTED(); @@ -596,21 +609,34 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          else              out += "light_vector = normalize(" + light_src + ".position + view);\n"; +        out += "spot_dir = " + light_src + ".spot_direction;\n"; +          // Compute dot product of light_vector and normal, adjust if lighting is one-sided or          // two-sided          std::string dot_product = light_config.two_sided_diffuse                                        ? "abs(dot(light_vector, normal))"                                        : "max(dot(light_vector, normal), 0.0)"; +        // If enabled, compute spot light attenuation value +        std::string spot_atten = "1.0"; +        if (light_config.spot_atten_enable && +            LightingRegs::IsLightingSamplerSupported( +                lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { +            std::string index = +                GetLutIndex(light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); +            auto sampler = LightingRegs::SpotlightAttenuationSampler(light_config.num); +            spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + +                         GetLutValue(sampler, index) + ")"; +        } +          // If enabled, compute distance attenuation value          std::string dist_atten = "1.0";          if (light_config.dist_atten_enable) {              std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " +                                  light_src + ".position) + " + light_src + ".dist_atten_bias)";              index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; -            const unsigned lut_num = -                ((unsigned)LightingRegs::LightingSampler::DistanceAttenuation + light_config.num); -            dist_atten = GetLutValue((LightingRegs::LightingSampler)lut_num, index); +            auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); +            dist_atten = GetLutValue(sampler, index);          }          // If enabled, clamp specular component if lighting result is negative @@ -711,11 +737,11 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {          // Compute primary fragment color (diffuse lighting) function          out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + -               light_src + ".ambient) * " + dist_atten + ";\n"; +               light_src + ".ambient) * " + dist_atten + " * " + spot_atten + ";\n";          // Compute secondary fragment color (specular lighting) function          out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + -               clamp_highlights + " * " + dist_atten + ";\n"; +               clamp_highlights + " * " + dist_atten + " * " + spot_atten + ";\n";      }      // Sum final lighting result @@ -967,6 +993,7 @@ struct LightSrc {      vec3 diffuse;      vec3 ambient;      vec3 position; +    vec3 spot_direction;      float dist_atten_bias;      float dist_atten_scale;  }; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index ea6d216d1..9c90eadf9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -93,6 +93,7 @@ union PicaShaderConfig {                  bool directional;                  bool two_sided_diffuse;                  bool dist_atten_enable; +                bool spot_atten_enable;              } light[8];              bool enable; @@ -110,7 +111,7 @@ union PicaShaderConfig {                  bool abs_input;                  Pica::LightingRegs::LightingLutInput type;                  float scale; -            } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; +            } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb;          } lighting;          struct {  | 
