mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 01:19:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			159 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 Dolphin Emulator Project
 | |
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| 
 | |
| #include "VideoCommon/UberShaderCommon.h"
 | |
| 
 | |
| #include "VideoCommon/NativeVertexFormat.h"
 | |
| #include "VideoCommon/ShaderGenCommon.h"
 | |
| #include "VideoCommon/VideoCommon.h"
 | |
| #include "VideoCommon/XFMemory.h"
 | |
| 
 | |
| namespace UberShader
 | |
| {
 | |
| void WriteLightingFunction(ShaderCode& out)
 | |
| {
 | |
|   // ==============================================
 | |
|   // Lighting channel calculation helper
 | |
|   // ==============================================
 | |
|   out.Write("int4 CalculateLighting(uint index, uint attnfunc, uint diffusefunc, float3 pos, "
 | |
|             "float3 normal) {{\n"
 | |
|             "  float3 ldir, h, cosAttn, distAttn;\n"
 | |
|             "  float dist, dist2, attn;\n"
 | |
|             "\n"
 | |
|             "  switch (attnfunc) {{\n");
 | |
|   out.Write("  case {:s}:\n", AttenuationFunc::None);
 | |
|   out.Write("  case {:s}:\n", AttenuationFunc::Dir);
 | |
|   out.Write("    ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
 | |
|             "    attn = 1.0;\n"
 | |
|             "    if (length(ldir) == 0.0)\n"
 | |
|             "      ldir = normal;\n"
 | |
|             "    break;\n\n");
 | |
|   out.Write("  case {:s}:\n", AttenuationFunc::Spec);
 | |
|   out.Write("    ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
 | |
|             "    attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS
 | |
|             "[index].dir.xyz)) : 0.0;\n"
 | |
|             "    cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n");
 | |
|   out.Write("    if (diffusefunc == {:s})\n", DiffuseFunc::None);
 | |
|   out.Write("      distAttn = " I_LIGHTS "[index].distatt.xyz;\n"
 | |
|             "    else\n"
 | |
|             "      distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n"
 | |
|             "    attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
 | |
|             "float3(1.0, attn, attn*attn));\n"
 | |
|             "    break;\n\n");
 | |
|   out.Write("  case {:s}:\n", AttenuationFunc::Spot);
 | |
|   out.Write("    ldir = " I_LIGHTS "[index].pos.xyz - pos.xyz;\n"
 | |
|             "    dist2 = dot(ldir, ldir);\n"
 | |
|             "    dist = sqrt(dist2);\n"
 | |
|             "    ldir = ldir / dist;\n"
 | |
|             "    attn = max(0.0, dot(ldir, " I_LIGHTS "[index].dir.xyz));\n"
 | |
|             "    attn = max(0.0, " I_LIGHTS "[index].cosatt.x + " I_LIGHTS
 | |
|             "[index].cosatt.y * attn + " I_LIGHTS "[index].cosatt.z * attn * attn) / dot(" I_LIGHTS
 | |
|             "[index].distatt.xyz, float3(1.0, dist, dist2));\n"
 | |
|             "    break;\n\n");
 | |
|   out.Write("  default:\n"
 | |
|             "    attn = 1.0;\n"
 | |
|             "    ldir = normal;\n"
 | |
|             "    break;\n"
 | |
|             "  }}\n"
 | |
|             "\n"
 | |
|             "  switch (diffusefunc) {{\n");
 | |
|   out.Write("  case {:s}:\n", DiffuseFunc::None);
 | |
|   out.Write("    return int4(round(attn * float4(" I_LIGHTS "[index].color)));\n\n");
 | |
|   out.Write("  case {:s}:\n", DiffuseFunc::Sign);
 | |
|   out.Write("    return int4(round(attn * dot(ldir, normal) * float4(" I_LIGHTS
 | |
|             "[index].color)));\n\n");
 | |
|   out.Write("  case {:s}:\n", DiffuseFunc::Clamp);
 | |
|   out.Write("    return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(" I_LIGHTS
 | |
|             "[index].color)));\n\n");
 | |
|   out.Write("  default:\n"
 | |
|             "    return int4(0, 0, 0, 0);\n"
 | |
|             "  }}\n"
 | |
|             "}}\n\n");
 | |
| }
 | |
| 
 | |
| void WriteVertexLighting(ShaderCode& out, APIType api_type, std::string_view world_pos_var,
 | |
|                          std::string_view normal_var, std::string_view in_color_0_var,
 | |
|                          std::string_view in_color_1_var, std::string_view out_color_0_var,
 | |
|                          std::string_view out_color_1_var)
 | |
| {
 | |
|   out.Write("// Lighting\n");
 | |
|   out.Write("for (uint chan = 0u; chan < {}u; chan++) {{\n", NUM_XF_COLOR_CHANNELS);
 | |
|   out.Write("  uint colorreg = xfmem_color(chan);\n"
 | |
|             "  uint alphareg = xfmem_alpha(chan);\n"
 | |
|             "  int4 mat = " I_MATERIALS "[chan + 2u]; \n"
 | |
|             "  int4 lacc = int4(255, 255, 255, 255);\n"
 | |
|             "\n");
 | |
| 
 | |
|   out.Write("  if ({} != 0u)\n", BitfieldExtract<&LitChannel::matsource>("colorreg"));
 | |
|   out.Write("    mat.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n",
 | |
|             in_color_0_var, in_color_1_var);
 | |
| 
 | |
|   out.Write("  if ({} != 0u)\n", BitfieldExtract<&LitChannel::matsource>("alphareg"));
 | |
|   out.Write("    mat.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", in_color_0_var,
 | |
|             in_color_1_var);
 | |
|   out.Write("  else\n"
 | |
|             "    mat.w = " I_MATERIALS " [chan + 2u].w;\n"
 | |
|             "\n");
 | |
| 
 | |
|   out.Write("  if ({} != 0u) {{\n", BitfieldExtract<&LitChannel::enablelighting>("colorreg"));
 | |
|   out.Write("    if ({} != 0u)\n", BitfieldExtract<&LitChannel::ambsource>("colorreg"));
 | |
|   out.Write("      lacc.xyz = int3(round(((chan == 0u) ? {}.xyz : {}.xyz) * 255.0));\n",
 | |
|             in_color_0_var, in_color_1_var);
 | |
|   out.Write("    else\n"
 | |
|             "      lacc.xyz = " I_MATERIALS " [chan].xyz;\n"
 | |
|             "\n");
 | |
|   out.Write("    uint light_mask = {} | ({} << 4u);\n",
 | |
|             BitfieldExtract<&LitChannel::lightMask0_3>("colorreg"),
 | |
|             BitfieldExtract<&LitChannel::lightMask4_7>("colorreg"));
 | |
|   out.Write("    uint attnfunc = {};\n", BitfieldExtract<&LitChannel::attnfunc>("colorreg"));
 | |
|   out.Write("    uint diffusefunc = {};\n", BitfieldExtract<&LitChannel::diffusefunc>("colorreg"));
 | |
|   out.Write(
 | |
|       "    for (uint light_index = 0u; light_index < 8u; light_index++) {{\n"
 | |
|       "      if ((light_mask & (1u << light_index)) != 0u)\n"
 | |
|       "        lacc.xyz += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).xyz;\n",
 | |
|       world_pos_var, normal_var);
 | |
|   out.Write("    }}\n"
 | |
|             "  }}\n"
 | |
|             "\n");
 | |
| 
 | |
|   out.Write("  if ({} != 0u) {{\n", BitfieldExtract<&LitChannel::enablelighting>("alphareg"));
 | |
|   out.Write("    if ({} != 0u) {{\n", BitfieldExtract<&LitChannel::ambsource>("alphareg"));
 | |
|   out.Write("      if ((components & ({}u << chan)) != 0u) // VB_HAS_COL0\n",
 | |
|             static_cast<u32>(VB_HAS_COL0));
 | |
|   out.Write("        lacc.w = int(round(((chan == 0u) ? {}.w : {}.w) * 255.0));\n", in_color_0_var,
 | |
|             in_color_1_var);
 | |
|   out.Write("      else if ((components & {}u) != 0u) // VB_HAS_COLO0\n",
 | |
|             static_cast<u32>(VB_HAS_COL0));
 | |
|   out.Write("        lacc.w = int(round({}.w * 255.0));\n", in_color_0_var);
 | |
|   out.Write("      else\n"
 | |
|             "        lacc.w = 255;\n"
 | |
|             "    }} else {{\n"
 | |
|             "      lacc.w = " I_MATERIALS " [chan].w;\n"
 | |
|             "    }}\n"
 | |
|             "\n");
 | |
|   out.Write("    uint light_mask = {} | ({} << 4u);\n",
 | |
|             BitfieldExtract<&LitChannel::lightMask0_3>("alphareg"),
 | |
|             BitfieldExtract<&LitChannel::lightMask4_7>("alphareg"));
 | |
|   out.Write("    uint attnfunc = {};\n", BitfieldExtract<&LitChannel::attnfunc>("alphareg"));
 | |
|   out.Write("    uint diffusefunc = {};\n", BitfieldExtract<&LitChannel::diffusefunc>("alphareg"));
 | |
|   out.Write("    for (uint light_index = 0u; light_index < 8u; light_index++) {{\n\n"
 | |
|             "      if ((light_mask & (1u << light_index)) != 0u)\n\n"
 | |
|             "        lacc.w += CalculateLighting(light_index, attnfunc, diffusefunc, {}, {}).w;\n",
 | |
|             world_pos_var, normal_var);
 | |
|   out.Write("    }}\n"
 | |
|             "  }}\n"
 | |
|             "\n");
 | |
| 
 | |
|   out.Write("  lacc = clamp(lacc, 0, 255);\n"
 | |
|             "\n"
 | |
|             "  // Hopefully GPUs that can support dynamic indexing will optimize this.\n"
 | |
|             "  float4 lit_color = float4((mat * (lacc + (lacc >> 7))) >> 8) / 255.0;\n"
 | |
|             "  switch (chan) {{\n"
 | |
|             "  case 0u: {} = lit_color; break;\n",
 | |
|             out_color_0_var);
 | |
|   out.Write("  case 1u: {} = lit_color; break;\n", out_color_1_var);
 | |
|   out.Write("  }}\n"
 | |
|             "}}\n"
 | |
|             "\n");
 | |
| }
 | |
| }  // namespace UberShader
 |