mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	renderer_opengl: Initial implementation of basic specular lighting.
This commit is contained in:
		
							parent
							
								
									e34fa6365f
								
							
						
					
					
						commit
						bf89870437
					
				
					 4 changed files with 165 additions and 13 deletions
				
			
		|  | @ -659,6 +659,8 @@ struct Regs { | ||||||
| 
 | 
 | ||||||
|     enum class LightingLutInput { |     enum class LightingLutInput { | ||||||
|         NH = 0, // Cosine of the angle between the normal and half-angle vectors
 |         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
 |         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)
 |         LightColor global_ambient; // emission + (material.ambient * lighting.ambient)
 | ||||||
|         INSERT_PADDING_WORDS(0x1); |         INSERT_PADDING_WORDS(0x1); | ||||||
|         BitField<0, 3, u32> src_num; // number of enabled lights - 1
 |         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 { |         union { | ||||||
|             // Each bit specifies whether distance attenuation should be applied for the
 |             // Each bit specifies whether distance attenuation should be applied for the
 | ||||||
|  |  | ||||||
|  | @ -297,6 +297,58 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | ||||||
|         SyncCombinerColor(); |         SyncCombinerColor(); | ||||||
|         break; |         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
 |     // Fragment lighting diffuse color
 | ||||||
|     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10): |     case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10): | ||||||
|         SyncLightDiffuse(0); |         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) { | void RasterizerOpenGL::SyncLightDiffuse(int light_index) { | ||||||
|     auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse); |     auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse); | ||||||
|     if (color != uniform_block_data.data.light_src[light_index].diffuse) { |     if (color != uniform_block_data.data.light_src[light_index].diffuse) { | ||||||
|  |  | ||||||
|  | @ -87,6 +87,10 @@ struct PicaShaderConfig { | ||||||
|             res.light_src[light_index].dist_atten_scale = Pica::float20::FromRawFloat20(light.dist_atten_scale).ToFloat32(); |             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; |         return res; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -118,6 +122,12 @@ struct PicaShaderConfig { | ||||||
| 
 | 
 | ||||||
|         bool lighting_enabled = false; |         bool lighting_enabled = false; | ||||||
|         unsigned num_lights = 0; |         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 { |     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; |         std::array<GLfloat, 3> diffuse; | ||||||
|         INSERT_PADDING_WORDS(1); |         INSERT_PADDING_WORDS(1); | ||||||
|         std::array<GLfloat, 3> ambient; |         std::array<GLfloat, 3> ambient; | ||||||
|  | @ -316,6 +330,12 @@ private: | ||||||
|     /// Syncs the specified light's position to match the PICA register
 |     /// Syncs the specified light's position to match the PICA register
 | ||||||
|     void SyncLightPosition(int light_index); |     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
 |     /// Syncs the remaining OpenGL drawing state to match the current PICA state
 | ||||||
|     void SyncDrawState(); |     void SyncDrawState(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -35,8 +35,7 @@ static void AppendSource(std::string& out, TevStageConfig::Source source, | ||||||
|         out += "primary_fragment_color"; |         out += "primary_fragment_color"; | ||||||
|         break; |         break; | ||||||
|     case Source::SecondaryFragmentColor: |     case Source::SecondaryFragmentColor: | ||||||
|         // HACK: Until we implement fragment lighting, use zero
 |         out += "secondary_fragment_color"; | ||||||
|         out += "vec4(0.0)"; |  | ||||||
|         break; |         break; | ||||||
|     case Source::Texture0: |     case Source::Texture0: | ||||||
|         out += "texture(tex[0], texcoord[0])"; |         out += "texture(tex[0], texcoord[0])"; | ||||||
|  | @ -334,6 +333,8 @@ in vec3 view; | ||||||
| out vec4 color; | out vec4 color; | ||||||
| 
 | 
 | ||||||
| struct LightSrc { | struct LightSrc { | ||||||
|  |     vec3 specular_0; | ||||||
|  |     vec3 specular_1; | ||||||
|     vec3 diffuse; |     vec3 diffuse; | ||||||
|     vec3 ambient; |     vec3 ambient; | ||||||
|     vec3 position; |     vec3 position; | ||||||
|  | @ -358,6 +359,7 @@ uniform sampler2D tex[3]; | ||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
| vec4 primary_fragment_color = vec4(0.0); | vec4 primary_fragment_color = vec4(0.0); | ||||||
|  | vec4 secondary_fragment_color = vec4(0.0); | ||||||
| )"; | )"; | ||||||
| 
 | 
 | ||||||
|     if (config.lighting_enabled) { |     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 += "    1.f - 2.f*(normquat.x*normquat.x + normquat.y*normquat.y)));\n"; | ||||||
|         out += "vec4 secondary_color = vec4(0.0);\n"; |         out += "vec4 secondary_color = vec4(0.0);\n"; | ||||||
|         out += "vec3 diffuse_sum = vec3(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 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) { |         for (unsigned light_index = 0; light_index < config.num_lights; ++light_index) { | ||||||
|             unsigned num = config.light_src[light_index].num; |             unsigned num = config.light_src[light_index].num; | ||||||
|             std::string light_src = "light_src[" + std::to_string(num) + "]"; |             std::string light_src = "light_src[" + std::to_string(num) + "]"; | ||||||
| 
 | 
 | ||||||
|             std::string light_vector; |  | ||||||
|             if (config.light_src[light_index].directional) |             if (config.light_src[light_index].directional) | ||||||
|                 light_vector = "normalize(-" + light_src + ".position)"; |                 out += "light_vector = normalize(-" + light_src + ".position);\n"; | ||||||
|             else |             else | ||||||
|                 light_vector = "normalize(" + light_src + ".position - fragment_position)"; |                 out += "light_vector = normalize(" + light_src + ".position - fragment_position);\n"; | ||||||
| 
 | 
 | ||||||
|             std::string dot_product; |             std::string dot_product; | ||||||
|             if (config.light_src[light_index].two_sided_diffuse) |             if (config.light_src[light_index].two_sided_diffuse) | ||||||
|                 dot_product = "abs(dot(" + light_vector + ", normal))"; |                 dot_product = "abs(dot(light_vector, normal))"; | ||||||
|             else |             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) { |             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 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 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 lut_index = "(" + scale + " * length(fragment_position - " + light_src + ".position) + " + bias + ")"; | ||||||
|                 std::string clamped_lut_index = "((clamp(int(" + lut_index + " * 256.0), 0, 255)))"; |                 std::string clamped_lut_index = "((clamp(int(" + lut_index + " * 256.0), 0, 255)))"; | ||||||
| 
 | 
 | ||||||
|                 unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + num); |                 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"; | ||||||
|                 dist_atten = "lighting_lut_" + std::to_string(lut_num /4) + "[" + clamped_lut_index + "][" + std::to_string(lut_num & 3) + "]"; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             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 += "diffuse_sum += lighting_global_ambient;\n"; | ||||||
|         out += "primary_fragment_color = vec4(clamp(diffuse_sum, vec3(0.0), vec3(1.0)), 1.0);\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
 |     // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue