mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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 { | ||||
|         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
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue