includes = [ "core/stingray_renderer/shader_libraries/common.shader_source", "core/stingray_renderer/shader_libraries/post_processing_common.shader_source" ]

/*
	TODO sort out code blocks later
	Put this code in a fatshark_postprocessing file and inherit filter from post_processing.
*/

render_states = {
	skydome_billboard = {
		inherits = "default"
		states = {
			z_write_enable = "false"

			blend_enable = "true"
			blend_op = "blend_op_add"
			dest_blend = "blend_inv_src_alpha"
			src_blend = "blend_src_alpha"
		}
	}

	filter = {
		inherits = "default"
		states = {
			z_write_enable = "false"
			z_enable = "false"
		}
	}

	filter_far = {
		inherits = "default"
		states = {
			z_enable = "true"
			z_func = "less_equal"
			z_write_enable = "false"
		}
	}

	filter_depth = {
		inherits = "default"
		states = {
			z_write_enable = "true"
			z_enable = "true"
			z_func = "always"
			write_mask0 = "0x0"
			write_mask1 = "0x0"
			write_mask2 = "0x0"
			write_mask3 = "0x0"
		}
	}

	init_linear_depth_div4 = {
		inherits = "filter"
		states = {
			write_mask0 = "red|green" // TODO: write_mask0 = "red", there is a bug when write_mask1 is the same as write_mask0
			write_mask1 = "red|green"
		}
	}

	filter_alpha = {
		inherits = "filter"
		states = {
			write_mask0 = "alpha"
		}
	}

	filter_specular_aa = {
		inherits = "filter"
		states = {
			defined_SECOND_PASS = {
				write_mask0 = "alpha"
			}
		}
	}

	filter_hdr_transparency = {
		inherits = "filter"
		states = {
			write_mask0 = "red|green|blue"
			blend_enable = "true"
			blend_op = "blend_op_add"
			src_blend = "blend_one"
			dest_blend = "blend_inv_src_alpha"
		}
	}

	filter_blend = {
		inherits = "filter"
		states = {
			blend_enable = "true"
			blend_op = "blend_op_add"
			src_blend = "blend_one"

			defined_PREMULTIPLIED = {
				dest_blend = "blend_inv_src_alpha"
			}
			ndefined_PREMULTIPLIED = {
				dest_blend = "blend_one"
			}

			defined_OUTLINE = {
				stencil_enable = "true"

				stencil_func = "not_equal"
				stencil_func_back_side = "not_equal"

				stencil_fail = "stencil_op_keep"
				stencil_pass = "stencil_op_keep"
				stencil_z_fail = "stencil_op_keep"

				stencil_fail_back_side = "stencil_op_keep"
				stencil_pass_back_side = "stencil_op_keep"
				stencil_z_fail_back_side = "stencil_op_keep"

				stencil_ref = "0x80"
				stencil_mask = "0x80"
			}

			defined_SKIN = {
				defined_D3D11 = {
					stencil_enable = "true"
					stencil_func = "equal"
					stencil_fail = "stencil_op_keep"
					stencil_pass = "stencil_op_keep"
					stencil_z_fail = "stencil_op_keep"

					stencil_func_back_side = "equal"
					stencil_fail_back_side = "stencil_op_keep"
					stencil_pass_back_side = "stencil_op_keep"
					stencil_z_fail_back_side = "stencil_op_keep"

					stencil_ref = "0x8"
					stencil_mask = "0x18"
					stencil_write_mask = "0x18"
				}
				defined_D3D12 = {
					stencil_enable = "true"
					stencil_func = "equal"
					stencil_fail = "stencil_op_keep"
					stencil_pass = "stencil_op_keep"
					stencil_z_fail = "stencil_op_keep"

					stencil_func_back_side = "equal"
					stencil_fail_back_side = "stencil_op_keep"
					stencil_pass_back_side = "stencil_op_keep"
					stencil_z_fail_back_side = "stencil_op_keep"

					stencil_ref = "0x8"
					stencil_mask = "0x18"
					stencil_write_mask = "0x18"
				}
			}
		}
	}

	skin_filter = {
		inherits = "default"
		states = {
			z_write_enable = "false"
			z_enable = "false"

			stencil_enable = "true"
			stencil_ref = "0x8"
			stencil_mask = "0x18"
			stencil_write_mask = "0x0"

			stencil_func = "equal"
			stencil_fail = "stencil_op_keep"
			stencil_pass = "stencil_op_keep"
			stencil_z_fail = "stencil_op_keep"

			stencil_func_back_side = "equal"
			stencil_fail_back_side = "stencil_op_keep"
			stencil_pass_back_side = "stencil_op_keep"
			stencil_z_fail_back_side = "stencil_op_keep"

			defined_STEP0 = {
				blend_factor_r = "0.3251"
				blend_factor_g = "0.45"
				blend_factor_b = "0.3583"
			}
			defined_STEP1 = {
				blend_factor_r = "0.34"
				blend_factor_g = "0.1864"
				blend_factor_b = "0.0"
			}
			defined_STEP2 = {
				blend_factor_r = "0.46"
				blend_factor_g = "0.0"
				blend_factor_b = "0.0402"
			}

			defined_INDEPENDENT_BLEND = {
				defined_DIRECTION_X = {
					independent_blend_enable = "false"
				}
				defined_DIRECTION_Y = {
					independent_blend_enable = "true"
					// blend_enable0 should be set to false but due to a bug in the latest nvidia driver that causes blending to get disabled for MRT1 as well..?!
					//blend_enable0 = "true"
					//src_blend0 = "blend_one"
					//dest_blend0 = "blend_zero"
					blend_enable0 = "false" // I'm pretty sure this bug has been resolved
					blend_enable1 = "true"
					src_blend1 = "blend_blend_factor"
					dest_blend1 = "blend_inv_blend_factor"
				}
			}

			ndefined_INDEPENDENT_BLEND = {
				defined_BLEND = {
					blend_enable = "true"
					src_blend = "blend_blend_factor"
					dest_blend = "blend_inv_blend_factor"
				}
			}
		}
	}

	skin_debug = {
		inherits = "default"
		states = {
			z_write_enable = "false"
			z_enable = "false"

			stencil_enable = "true"
			defined_SKIN = {
				stencil_ref = "0x8"
			}
			ndefined_SKIN = {
				stencil_ref = "0x0"
			}
			stencil_mask = "0x18"
			stencil_write_mask = "0x8"

			stencil_func = "equal"
			stencil_fail = "stencil_op_keep"
			stencil_pass = "stencil_op_keep"
			stencil_z_fail = "stencil_op_keep"

			stencil_func_back_side = "equal"
			stencil_fail_back_side = "stencil_op_keep"
			stencil_pass_back_side = "stencil_op_keep"
			stencil_z_fail_back_side = "stencil_op_keep"
		}
	}

	outline_stencil = {
		inherits = "filter"
		states = {
			stencil_enable = "true"

			defined_FILL = {
				stencil_func = "equal"
				stencil_func_back_side = "equal"
			}
			ndefined_FILL = {
				stencil_func = "not_equal"
				stencil_func_back_side = "not_equal"

				ndefined_OPAQUE = {
					blend_enable = "true"
					blend_op = "blend_op_add"
					src_blend = "blend_one"
					dest_blend = "blend_inv_src_alpha"
				}
			}

			stencil_fail = "stencil_op_keep"
			stencil_pass = "stencil_op_keep"
			stencil_z_fail = "stencil_op_keep"

			stencil_fail_back_side = "stencil_op_keep"
			stencil_pass_back_side = "stencil_op_keep"
			stencil_z_fail_back_side = "stencil_op_keep"

			stencil_ref = "0x80"
			stencil_mask = "0x80"
			stencil_write_mask = "0x0"
		}
	}

	fog_plane = {
		inherits = "opacity"
		states = {
			defined_INVERTED = {
				z_func = "greater"
			}

			z_enable = "true"

			blend_op = "blend_op_add"
			src_blend = "blend_one"
			dest_blend = "blend_inv_src_alpha"

			defined_LOW_RES = {
				write_mask0 = "red|green|blue|alpha"
				write_mask1 = "red|green"
			}
			ndefined_LOW_RES = {
				write_mask0 = "red|green|blue"
			}
		}
	}


	premultiplied_alpha = {
		inherits = "opacity"
		states = {
			src_blend = "blend_one"
			dest_blend = "blend_inv_src_alpha"
		}
	}

	opacity_volume = {
		inherits = "opacity"
		states = {
			defined_ENABLE_INSIDE= {
				cull_mode = "cull_ccw"
				z_enable = "false"	
			}
			ndefined_ENABLE_INSIDE= {
				cull_mode = "cull_cw"
				z_enable = "true"
			}

			blend_op = "blend_op_add"
			src_blend = "blend_one"
			dest_blend = "blend_inv_src_alpha"

			defined_LOW_RES = {
				write_mask0 = "red|green|blue|alpha"
				write_mask1 = "red|green"
			}
			ndefined_LOW_RES = {
				write_mask0 = "red|green|blue"
			}
		}
	}
}

sampler_states = {
}

hlsl_shaders = {
	error_debug = {
		includes = [ "common" ]

		code="""
			Texture2D input_texture0; 

			struct VS_INPUT {
				float4 position : POSITION;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				return o;
			}
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				float4 texel = input_texture0.Load(int3(input.position.xy, 0));
				float4 result = 0.0;
				if(any(isinf(texel)))
					result.r = 1.0;
				if(any(isnan(texel)))
					result.g = 1.0;
				if(any(texel < 0.0))
					result.b = 1.0;
				if(!any(isfinite(texel)))
					result.a = 1.0;
				return result;
			}
		"""
	}

	init_luminance_adaptation = {
		includes = [ "common" ]

		code="""
			struct VS_INPUT {
				float4 position : POSITION;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				return o;
			}
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				return half4(0.5,0.5,0.5,0.5);
			}
		"""
	}

	average_luminance_feedback = {		
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
			input_texture1 = { sampler_states = "clamp_linear" }
		}

		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(input_texture1);

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float2 inv_input_texture0_size;
				float3 eye_adaptation_speed_min_max;			
			CBUFFER_END
			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;
				
				return o;
			}			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) : SV_TARGET0 {				
				float d = inv_input_texture0_size.x;
				// TODO: expose a luminance to exposure curve (see Cryteks solution). 
				float current_avg_luminance = exp((
					TEX2D(input_texture0, input.uv + float2(-d, -d)).a +
					TEX2D(input_texture0, input.uv + float2( d, -d)).a +
					TEX2D(input_texture0, input.uv + float2(-d,  d)).a +
					TEX2D(input_texture0, input.uv + float2( d,  d)).a) * 0.25);

				//float adapted_luminance = TEX2D(input_texture1, input.uv).r;
				float2 eye_adaption_uv = viewport.zw + viewport.xy * 0.5;
				float adapted_luminance = TEX2D(input_texture1, eye_adaption_uv).r;
				
				// isfinite is a safety check if there is protection if there is incorrect data in the luminance pass. Might consider to do this in the bright pass.
				return isfinite(current_avg_luminance) ? (adapted_luminance + (clamp(current_avg_luminance, eye_adaptation_speed_min_max.y, eye_adaptation_speed_min_max.z) - adapted_luminance) * (1.0 - exp(-delta_time * eye_adaptation_speed_min_max.x))).rrrr : adapted_luminance.rrrr;
			}	
		"""
	}


	volume_height_fog = {
		includes = [ "common", "gbuffer_access", "fog"]
		samplers = {
			diffuse_map = { sampler_states ="wrap_linear" }
		}

		code="""
			#ifdef LOW_RES
				Texture2D<float4> linear_depth_div4;
			#else
				Texture2D<float4> linear_depth;
			#endif
			
			#if defined(DENSITY_MAP)
				DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Density Map" type="resource" }
			#endif

			struct VS_INPUT {
				float4 position : POSITION;
				float3 normal : NORMAL0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float4 w : TEXCOORD0;
				float wz_root : TEXCOORD1;
				float linear_depth : TEXCOORD2;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float4x4 world;
				float4x4 inv_world;

				#if defined(SOFT_VOLUME)
					float4x4 bounding_volume;
				#endif

				float3 height_fog_settings; 	// exports={ name="Fog Settings [Edge Falloff, Falloff Z, Density]" type="vector3" value=[0.1 0.747 1.0] min=[0 0 0] max=[2 10 1] step=[0.001 0.001 0.001] }
				float3 height_fog_color; 		// exports={ name="Fog Color" type="vector3" value=[0.5 0.6 0.7] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] }
				float3 height_fog_sun_blend; 	// exports={ name="Fog Sun [Blend, Exponent, Strength]" type="vector3" value=[1 4 1] min=[0 1 0] max=[1 16 1] step=[0.001 0.001 0.001] }
				#if defined(DENSITY_MAP)
					float3 height_fog_map_scale; 	// exports={ name="Density Map Scale #1" type="vector3" value=[0.7 0.7 10.0] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] }
					float height_fog_map_strength; 	// exports={ name="Density Map Strength #1" type="scalar" value=0.6 min=0 max=1 step=0.001 }
					float3 height_fog_map_wind; 	// exports={ name="Density Map Wind #1" type="vector3" value=[0.015 0.0 0.07] min=[-3 -3 -3] max=[3 3 3] step=[0.001 0.001 0.001] }
					float3 height_fog_map_scale2; 	// exports={ name="Density Map Scale #2" type="vector3" value=[0.4 0.4 0.6] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] }
					float height_fog_map_strength2;	// exports={ name="Density Map Strength #2" type="scalar" value=1.0 min=0 max=1 step=0.001 }
					float3 height_fog_map_wind2; 	// exports={ name="Density Map Wind #2" type="vector3" value=[0.045 0.03 -0.07] min=[-3 -3 -3] max=[3 3 3] step=[0.001 0.001 0.001] }
				#endif
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;

				o.position = mul(input.position, world_view_proj);
				o.w = encode_world_pos(o.position, camera_unprojection);
				o.wz_root = world._m32; // wp.z
				#if defined(GL2)
					o.linear_depth = linearize_depth(o.position.z*0.5 / o.position.w + 0.5);
				#else
					o.linear_depth = linearize_depth(o.position.z / o.position.w);
				#endif

				return o;
			}

			float density_scale(float3 wp, float3 view_dir, float3 wind, Sampler2D map, float3 scale) {
				float bias = 0.0;
				[unroll]
				for (int i = 0; i < 4; ++i) {
					float3 sample_pos = wp + view_dir * 0.25 * i;
					float3 uv = (sample_pos * scale + time * wind);

					bias += (TEX2D(map, uv.xy).r + TEX2D(map, uv.xz).r) * 0.5;
				}
				return bias * 0.25;
			}

			// N = plane normal
			// O = ray origin point
			// d = distance of the plane from the origin
			// D = normalized ray direction vector
			float4 ray_plane_intersection(float3 N, float3 O, float d, float3 D, float cull) {
				if(cull == -1 && dot(N, D) > 0 || cull == 1 && dot(N, D) < 0)
					return float4(0, 0, 0, camera_near_far.y);

				float t = -(dot(N, O) + d) / (dot(N, D));	// t = distance from origin to intersection

				return float4(O + t * D, t);
			}
 
			#ifdef LOW_RES
				struct PS_OUTPUT {
					half4 color : SV_TARGET0;
					half4 depth : SV_TARGET1;
				};

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_OUTPUT ps_main(PS_INPUT input)
			#else
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				half4 ps_main(PS_INPUT input) : SV_TARGET0
			#endif
			{
				#ifdef LOW_RES
					float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0)));
				#else
					float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0)));
				#endif

				float3 wp = decode_world_pos(input.w, d);
				float3 op = mul(float4(wp, 1), inv_world);
				float3 wp_cam = camera_world._m30_m31_m32;
				float3 half_volume = float3(10,10,10) * 0.5;
				float falloff_xy = height_fog_settings.x;
				float falloff_z = height_fog_settings.y;
				float density = height_fog_settings.z;

				// Inside fog volume?
				bool is_inside = abs(op.x) < half_volume.x && abs(op.y) < half_volume.y;
				#if defined(ENABLE_INSIDE) || defined(LOW_RES)
					if (!is_inside && input.linear_depth > d)
						discard;
				#endif

				#if defined(SOFT_VOLUME)
					float4 op_cam = mul(float4(wp_cam, 1), inv_world);
					float3 o_view_dir = normalize(op - op_cam);

					if (!is_inside) {
						// TODO: we already have the linear depth, we don't need these calculations if the length( wp_start - wp) calculation is done in linear depth space.
						float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1);
						float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1);
						float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1);
						float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1);
						float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, o_view_dir, -1);

						float4 p = p1;
						p = p.w < p2.w ? p : p2;
						p = p.w < p3.w ? p : p3;
						p = p.w < p4.w ? p : p4;
						p = p.w < p5.w ? p : p5;

						wp = mul(float4(p.xyz, 1), world);
						op = p.xyz;
					}

					float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, -o_view_dir, -1);
					float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, -o_view_dir, -1);
					float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, -o_view_dir, -1);
					float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, -o_view_dir, -1);
					float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, -o_view_dir, -1);

					float4 start = p1;
					start = start.w < p2.w ? start : p2;
					start = start.w < p3.w ? start : p3;
					start = start.w < p4.w ? start : p4;
					start = start.w < p5.w ? start : p5;

					float radius = length((bounding_volume._m00_m01_m02 - bounding_volume._m10_m11_m12) * 0.5);
					float3 wp_start = mul(float4(start.xyz, 1), world);

					density *= clamp( (length( wp_start - wp)/radius) * falloff_xy, 0, 1.0);
				#else
					if(!is_inside) {
						float4 op_cam = mul(float4(wp_cam, 1), inv_world);
						float3 o_view_dir = normalize(op - op_cam);

						float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1);
						float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1);
						float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1);
						float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1);
						float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, o_view_dir, -1);

						float4 p = p1;
						p = p.w < p2.w ? p : p2;
						p = p.w < p3.w ? p : p3;
						p = p.w < p4.w ? p : p4;
						p = p.w < p5.w ? p : p5;

						wp = mul(float4(p.xyz, 1), world);
						op = p.xyz;
					}
				#endif

				float3 view_vec = wp - wp_cam;
				float view_dist = length(view_vec);
				float3 view_dir = normalize(view_vec);

				// Fade edges
				/*if(is_inside && falloff_xy > 0){
					density *= saturate((half_volume.x - abs(op.x)) / falloff_xy) * saturate((half_volume.y - abs(op.y)) / falloff_xy);

					float2 uv = (op.xy/half_volume) * 0.5 + 0.5;
					#if defined(DENSITY_MAP)
						density *= TEX2D(diffuse_map, uv).b;
					#endif
				}*/

				// Density map
				#if defined(DENSITY_MAP)
					float ds1 = density_scale(wp, view_dir, height_fog_map_wind, diffuse_map, height_fog_map_scale) * height_fog_map_strength;
					float ds2 = density_scale(wp, view_dir, height_fog_map_wind2, diffuse_map, height_fog_map_scale2) * height_fog_map_strength2;
					density *= (ds1 + ds2) * 0.5;
				#endif

				#if defined(Z_INVERSION)
					if( wp_cam.z > wp.z )
						discard;
					float fog_a = density * saturate( 1.0 - exp((-wp_cam.z + input.wz_root) * falloff_z) * (exp(-view_dist * view_dir.z * falloff_z)) / view_dir.z );
				#else
					// Fog alpha
					float fog_a = density * saturate( exp((-wp_cam.z + input.wz_root) * falloff_z) * (1.0-exp(-view_dist * view_dir.z * falloff_z)) / view_dir.z );
				#endif

				// Sun alpha
				half sun_a = height_fog_sun_blend.x * pow(saturate(dot(view_dir, -sun_direction)), height_fog_sun_blend.y);

				// Mix sun & fog color
				float3 c = lerp(height_fog_color, height_fog_sun_blend.z * sun_color, sun_a);
				half4 result = apply_fog(float4(c, fog_a), wp, input.linear_depth);
				
				#ifdef LOW_RES
					const float3 camera_dir = camera_world._m10_m11_m12;
					float back_depth = dot(view_vec, camera_dir);

					PS_OUTPUT o;
					o.color = half4(result.rgb * result.a, result.a);
					float alpha_depth = back_depth * result.a;
					o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, result.a);
					return o;
				#else
					return half4(result.rgb * result.a, result.a);
				#endif
			}
		"""
	}

	fog_plane = { 
		includes = [ "common", "gbuffer_access", "fog"]

		samplers = {
			diffuse_map = { sampler_states ="wrap_linear" }
			ndefined_LOW_RES = {
				defined_LOW_RES_ENABLED = {
					hdr_transparent_div4 = { sampler_states = "clamp_linear" }
					hdr_linear_depth_div4 = { sampler_states = "clamp_linear" }
				}
			}
			defined_AMBIENT_TINT = {
				global_diffuse_map = { sampler_states = "clamp_linear"}
			}
		}
		
		code="""				
			#ifdef LOW_RES
				Texture2D<float4> linear_depth_div4;
			#else
				Texture2D<float4> linear_depth;
				#if defined(LOW_RES_ENABLED)
					DECLARE_SAMPLER_2D(hdr_transparent_div4);
					DECLARE_SAMPLER_2D(hdr_linear_depth_div4);
				#endif
			#endif

			#if defined(AMBIENT_TINT)
				DECLARE_SAMPLER_CUBE(global_diffuse_map);
			#endif

			
			struct VS_INPUT {
				float4 position : POSITION;
				float3 normal : NORMAL;
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float4 plane_eq : TEXCOORD0;
				float4 w : TEXCOORD1;
				float linear_depth : TEXCOORD2;
				#if defined(AMBIENT_TINT)
					float3 ambient_color : 	TEXCOORD3;
				#endif
			};			
			
			CBUFFER_START(c0)
				float4x4 world;
				float4x4 view_proj;
				float3 fog_plane_color; // exports={ name="Fog Color" type="vector3" value=[0.5 0.5 0.5] min=[0 0 0] max=[8 8 8] step=[0.001 0.001 0.001] }
				float fog_distance; 	// exports={ name="Fog Distance" type="scalar" value=10 min=0.01 max=200 step=0.01 }
				#if defined(AMBIENT_TINT)
					float3 ambient_tint;
				#endif
			CBUFFER_END
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
								
				float3 wp = mul(input.position, world);
				o.position = mul(float4(wp,1), view_proj);

				float3 wnormal = mul(input.normal, (float3x3)world);
				o.plane_eq = float4(wnormal, dot(wnormal, wp));
				
				o.w = encode_world_pos(o.position, camera_unprojection);

				#if defined(GL2)
					o.linear_depth = linearize_depth(o.position.z*0.5 / o.position.w + 0.5);
				#else
					o.linear_depth = linearize_depth(o.position.z / o.position.w);
				#endif

				#if defined(AMBIENT_TINT)
					#if defined(D3D11)
						ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint;
					#endif
					o.ambient_color = rgbm_decode(TEXCUBELOD(global_diffuse_map, normalize(wnormal), 0)) * ambient_tint * fog_plane_color;
				#endif

				return o;
			}
			
			#ifdef LOW_RES
				struct PS_OUTPUT {
					half4 color : SV_TARGET0;
					half4 depth : SV_TARGET1;
				};

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_OUTPUT ps_main(PS_INPUT input)
			#else
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				half4 ps_main(PS_INPUT input) : SV_TARGET0
			#endif
			{					
				#ifdef LOW_RES
					float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0)));
				#else
					float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0)));
				#endif

				#if defined(MASK_OUT_SKYDOME)
					if (d > camera_near_far.y) discard;
				#endif

				float3 wp = decode_world_pos(input.w, d);
				
				float distance_to_plane = abs(dot(wp, input.plane_eq.xyz) - input.plane_eq.w);
				
				#if defined(AMBIENT_TINT)
					float3 color = input.ambient_color;
				#else
					float3 color = fog_plane_color;
				#endif	
				half4 result = 	apply_fog(float4(color, saturate(distance_to_plane / fog_distance)), wp, input.linear_depth);
				
				#ifdef LOW_RES
					PS_OUTPUT o;
					o.color = half4(result.rgb * result.a, result.a);
					float alpha_depth = input.linear_depth * result.a;
					o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, result.a);
					return o;
				#else
					#if defined(LOW_RES_ENABLED)
						// Fade out high resolution particle if the low resolution particles is infront of the high resolution particle
						float2 screen_uv = input.position.xy / back_buffer_size;
						float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg;
						float depth_mean = particle_depth_values.x;
						float depth_variance = particle_depth_values.y - depth_mean*depth_mean;
						float background_depth = input.linear_depth;
						
						float diff = max(background_depth - depth_mean, 0.0);
						float C = 1.38629436112; // 2 * ln(2)
						float T = exp2(-diff*diff / (C * max(depth_variance, 0.001)));
						float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T);
						result.a *= 1.0 - alpha_modifier;
					#endif

					return half4(result.rgb * result.a, result.a);
				#endif		
			}	
		"""
	}	

	light_shafts = {		
		includes = [ "common", "gbuffer_access", "color_management" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
			linear_depth = { sampler_states = "clamp_point" }
			luminance_adaptation = { sampler_states = "clamp_linear" }
		}
		 
		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(linear_depth);
			#if defined(EYE_ADAPTATION)
				DECLARE_SAMPLER_2D(luminance_adaptation);
			#endif
			
			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
				float2 sun_screen_pos : TEXCOORD2;
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float3 sun_direction;
				#if defined(BRIGHT_PASS)
					float3 sun_color;
					float exposure;
					float2 light_shaft_settings;
					float2 input_texture0_size;
				#else
					float light_shaft_weigth;
				#endif	
			CBUFFER_END
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);				
				o.uv = input.uv;
				
				float4 sun_pos = float4(-sun_direction, 1.0);
				camera_view._m30_m31_m32 = float3(0,0,0);
				sun_pos = mul( mul( sun_pos, camera_view ), camera_projection );
				sun_pos /= sun_pos.w;
				o.sun_screen_pos = sun_pos.xy * float2(0.5, -0.5) + 0.5;
				
				return o;
			}			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) : SV_TARGET0 
			{
				half2 uv = input.uv;
				float3 c = TEX2D( input_texture0, uv );
				
				#if defined(BRIGHT_PASS)
					#if !defined(D3D11)
						const bool capture_cubemap = false; 
					#endif
					if (!capture_cubemap) {
						#ifdef EYE_ADAPTATION
							c *= exposure / TEX2D(luminance_adaptation, uv).r;
						#else
							c *= exposure;
						#endif
					}

					float d = gbuffer_decode_depth(TEX2D(linear_depth, uv));

					// Render occluding objects black
					if(d < camera_near_far.y*0.99 ){
						return float4( 0, 0, 0, 1 );
					}

					c.rgb *= light_shaft_settings.x;
					c = safe_range_tone_map(c);

					// Render light source (sun)
					float2 delta = uv - input.sun_screen_pos;
					float dist = length( delta * float2( input_texture0_size.x/input_texture0_size.y, 1.0 ) );
					
					c *= sun_color * (1.0 - saturate(dist/light_shaft_settings.y));
				#else
					uint NUM_SAMPLES = 50;
					float density = 0.90;
					float decay = 0.992;
					float weight = light_shaft_weigth;
					
					// Calculate vector from pixel to light source in screen space
					
					half2 delta_uv = uv - input.sun_screen_pos;
					
					// Divide by number of samples and scale by control factor. 
					delta_uv *= 1.0f / NUM_SAMPLES * density;  
					
					// Set up illumination decay factor.  
					half illumination_decay = 1.0f;

					// Retrieve samples at new location.  
					const float weights[] = {0.25, 0.5, 0.25};
					
					// Evaluate summation from Equation 3 NUM_SAMPLES iterations.  
					for (uint i = 0; i < NUM_SAMPLES; i++)  
					{  						
	
						float4 s = TEX2D(input_texture0, uv - delta_uv / 3) * weights[0];
						s += TEX2D(input_texture0, uv) * weights[1];
						s += TEX2D(input_texture0, uv + delta_uv / 3) * weights[2];
						
						// Apply attenuation decay and weight factor
						s *= illumination_decay * weight; 
						
						// Accumulate combined color.
						c += s.rgb;
						
						// Update exponential decay factor.  
						illumination_decay *= decay;
						
						// Step sample location along ray. 
						uv -= delta_uv;
					}
				#endif

				return float4(c, 1.0);
			}
		"""
	}

	skydome_billboard = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			diffuse_map = { sampler_states = "wrap_linear" }
		}

		code="""
			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv 		: TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv 		: TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 view;
				float4x4 proj;
				#if defined(SECONDARY_SUN)
					float3 secondary_sun_direction;
					#define direction secondary_sun_direction
				#else
					float3 sun_direction;
					#define direction sun_direction
				#endif
				float scale; // exports={ name="Scale" type="scalar" value=1 min=0 max=2 step=0.001 }
			CBUFFER_END

			DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" }

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;

				float4 position = input.position * scale;
				
				float4 wp = float4(-direction, 1.0);
				float3 y_axis = float3(0, 0, 1);
				float3 x_axis = normalize(cross(y_axis, direction));
				y_axis = cross(direction, x_axis);
				wp.rgb += x_axis * position.x + y_axis * position.y;

				o.uv = input.uv;

				view._m30_m31_m32 = float3(0,0,0);
				o.position = mul(mul(wp, view), proj);
				o.position.z = o.position.w;

				return o;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				return TEX2D(diffuse_map, input.uv);
			}
		"""
	}

	fixed_function_blend = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			defined_POINT_SAMPLER = {
				input_texture0 = { sampler_states = "wrap_point" }
			}
			ndefined_POINT_SAMPLER = {
				input_texture0 = { sampler_states = "wrap_linear" }
			}
		}

		code="""
			DECLARE_SAMPLER_2D(input_texture0);

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv 		: TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv 		: TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;
				return o;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) : SV_TARGET0 {
				float4 color = TEX2D(input_texture0, input.uv);
				#if defined(OUTLINE)
					color.a = max(color.r, max(color.g, color.b));
				#endif
				return color;
			}
		"""
	}

	skin_filter = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
			input_texture1 = { sampler_states = "clamp_point" }
		}

		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(input_texture1);

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;

				return o;
			}

			#if defined(BLEND)
				float4 blend_result(PS_INPUT input) {
					return TEX2D(input_texture0, input.uv);
				}
			#else
				float4 skin_blur(PS_INPUT input, uniform float2 step_value) : SV_TARGET0 {
					// Gaussian weights for the six samples around the current pixel:
					//   -3 -2 -1 +1 +2 +3
					float w[6] = { 0.006,   0.061,   0.242,  0.242,  0.061, 0.006 };
					float o[6] = {  -1.0, -0.6667, -0.3333, 0.3333, 0.6667,   1.0 };

					// Fetch color and linear depth for current pixel:
					float4 colorM = TEX2D(input_texture0, input.uv);
					float depthM = TEX2D(input_texture1, input.uv);

					// Accumulate center sample, multiplying it with its gaussian weight:
					float4 colorBlurred = colorM;
					colorBlurred.rgb *= 0.382;

					// Calculate the step that we will use to fetch the surrounding pixels,
					// where "step" is:
					//     step = sssStrength * gaussianWidth * pixelSize * dir
					// The closer the pixel, the stronger the effect needs to be, hence
					// the factor 1.0 / depthM.
					//colorM.a = 1;
					float2 finalStep = colorM.a * step_value / depthM;
					float correction = 1600; //8000;

					// Accumulate the other samples:
					[unroll]
					for (int i = 0; i < 6; i++) {
						// Fetch color and depth for current sample:
						float2 offset = input.uv + o[i] * finalStep;
						float3 color = TEX2D(input_texture0, offset).rgb;
						float depth = TEX2D(input_texture1, offset).r;

						// If the difference in depth is huge, we lerp color back to "colorM":
						float s = min(0.0125 * correction * abs(depthM - depth), 1.0);
						color = lerp(color, colorM.rgb, s);

						// Accumulate:
						colorBlurred.rgb += w[i] * color;
					}

					// The result will be alpha blended with current buffer by using specific
					// RGB weights. For more details, I refer you to the GPU Pro chapter :)
					return colorBlurred;
				}

				float4 skin_main(PS_INPUT input) {
					float sss_strength = 12.5; //31.5;
					float maxdd = 0.001;

					#ifdef STEP0
						float gaussian_width = sqrt((0.0516 - 0.0064));
					#elif defined(STEP1)
						float gaussian_width = sqrt((0.2719 - 0.0516));
					#elif defined(STEP2)
						float gaussian_width = sqrt((2.0062 - 0.2719));
					#endif

					#ifdef DIRECTION_X
						float2 dir = float2(1,0);
					#else
						float2 dir = float2(0,1);
					#endif

					float2 step_val = sss_strength * gaussian_width * (float2(1,1) / back_buffer_size.xy) * dir;

					float4 c = skin_blur(input, step_val);

					return c;
				}
			#endif

			#if defined(DIRECTION_X) || (defined(DIRECTION_Y) && !defined(INDEPENDENT_BLEND))
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				float4 ps_main(PS_INPUT input) : SV_TARGET0 {
					return skin_main(input);
				}
			#elif defined(BLEND)
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				float4 ps_main(PS_INPUT input) : SV_TARGET0 {
					return blend_result(input);
				}
			#else
				struct PS_OUTPUT {
					float4 acc : SV_TARGET0;
					float4 final : SV_TARGET1;
				};

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_OUTPUT ps_main(PS_INPUT input) {
					PS_OUTPUT o;
					float4 c = skin_main(input);
					o.acc = c;
					o.final = c;
					return o;
				}
			#endif
		"""
	}

	add_colored_outline = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
			input_texture1 = { sampler_states = "clamp_point" }
		}

		code="""
			#ifndef FILL
				DECLARE_SAMPLER_2D(input_texture0);
				DECLARE_SAMPLER_2D(input_texture1);
			#endif

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
				#ifndef FILL
					float4 outline_thickness;			

					float3 outline_color_red;
					float3 outline_color_green;
					float3 outline_color_blue;
					float3 outline_color_alpha;

					float outline_multiplier_red;
					float outline_multiplier_green;
					float outline_multiplier_blue;
					float outline_multiplier_alpha;
				#endif
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;

				return o;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) : SV_TARGET0 {
				#ifdef FILL
					return float4(0, 0, 0, 0);
				#else
					// float mask = 1.0 - TEX2D(input_texture1, input.uv).r;
					float4 color = TEX2D(input_texture0, input.uv);

					color = pow(color, outline_thickness);

					float3 	final_color = 	color.r * outline_color_red * outline_multiplier_red;
							final_color += 	color.g * outline_color_green * outline_multiplier_green;
							final_color += 	color.b * outline_color_blue * outline_multiplier_blue;
							final_color += 	color.a * outline_color_alpha * outline_multiplier_alpha;

					// final_color *= mask;

					// We assume that the colors was authered in gamma 2.2
					final_color = pow(final_color, 2.2/gamma);
					float alpha = saturate(max(final_color.r, max(final_color.g, final_color.b)));
					return float4(final_color, alpha);
				#endif
			}
		"""
	}

	specular_aa = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
		}

		code="""
			#define NUM_GAUSSIAN_WEIGHTS 4

			// Gaussian coefficients
			static float gaussian_weights[] =
			//	{ 0.398943, 0.241971, 0.053991, 0.004432, 0.000134 };  // stddev = 1.0
				{ 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 };  // stddev = 2.0
			//	{ 0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108 }; // stddev = 3.0

			// First pass:
			// Decode and blur normals
			// Second pass:
			// Blur normals and encode new roughness
			#if defined(FIRST_PASS)
				static int2 axis = int2(1,0);
				#define NORMALIZE(x) normalize(x)
				#define DECODE_NORMAL(x) gbuffer_decode_normal(x)
				#define DECODE_ROUGHNESS(x) gbuffer_decode_roughness(x)
			#else
				static int2 axis = int2(0,1);
				#define NORMALIZE(x) x
				#define DECODE_NORMAL(x) x.rgb
				#define DECODE_ROUGHNESS(x) x.a
			#endif

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			Texture2D<float4> input_texture0;
			Texture2D<float4> linear_depth;

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				float4 p = mul(input.position, world_view_proj);
				o.position = p;
				o.uv = input.uv;

				return o;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				int2 position = input.position.xy;

				half4 texel = input_texture0.Load(int3(position, 0));
				float3 ref_N = NORMALIZE(DECODE_NORMAL(texel));
				float ref_roughness = DECODE_ROUGHNESS(texel);
				float ref_depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0)));

				// Base weight for depth falloff. Decrease this for more blurriness,
				// increase it for better edge discrimination
				float total_weight = gaussian_weights[0] * 0.5;
				float3 Na = ref_N * total_weight;

				const float DEPTH_THRESHOLD = 0.2;
				const float inv_denominator = 1.0 / (-DEPTH_THRESHOLD);

				position -= axis * NUM_GAUSSIAN_WEIGHTS;
				[unroll]
				for (int r = NUM_GAUSSIAN_WEIGHTS; r > 0 ; --r) {
					// Sample data
					float3 N = NORMALIZE(DECODE_NORMAL(input_texture0.Load(int3(position, 0))));
					float depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0)));
					float weight = gaussian_weights[r];

					// Range domain (the "bilateral" weight). As depth difference increases, decrease weight.
					weight *= saturate(abs(ref_depth - depth) * inv_denominator + 1.0);  //linearstep is cheaper, smoothstep(DEPTH_THRESHOLD, 0, abs(ref_depth - depth));

					Na += N * weight;
					total_weight += weight;

					position += axis;
				}

				// The case where r == 0 is handled above the for loop, just change the sample pos here and go into the next loop.
				position = input.position.xy + axis;
				[unroll]
				for (int r = 1; r <= NUM_GAUSSIAN_WEIGHTS; ++r) {
					// Sample data
					float3 N = NORMALIZE(DECODE_NORMAL(input_texture0.Load(int3(position, 0))));
					float depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0)));
					float weight = gaussian_weights[r];

					// Range domain (the "bilateral" weight). As depth difference increases, decrease weight.
					weight *= saturate(abs(ref_depth - depth) * inv_denominator + 1.0); //linearstep is cheaper, smoothstep(DEPTH_THRESHOLD, 0, abs(ref_depth - depth));

					Na += N * weight;
					total_weight += weight;

					position += axis;
				}

				Na = Na / total_weight;

				#if defined(FIRST_PASS)
					return half4(Na, ref_roughness);
				#else
					// Specular AA
					// http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
					float l = length(Na);
					[flatten]
					if (l < 0.999) {
						float ll = l * l ;
						float kappa = (3.0 * l - l * ll) / (1.0 - ll) ;
						float variance = 1.0 / kappa;
						ref_roughness = sqrt(ref_roughness * ref_roughness + variance);
					}

					return half4(0, 0, 0, gbuffer_encode_roughness(ref_roughness));
				#endif
			}
		"""
	}

	apply_hdr_transparent = {
		includes = [ "common", "gbuffer_access" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_linear" }
			input_texture1 = { sampler_states = "clamp_linear" }
		}

		compiler_options = {
			D3D11 = {
				pixel_shader = {					
					instruction_set = "ps_5_0"
				}				
			}
			D3D12 = {
				pixel_shader = {					
					instruction_set = "ps_5_0"
				}				
			}
		}

		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(input_texture1);

			Texture2D linear_depth;

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;

				return o;
			}

			// Upsampling method based on Destiny VDP
			// http://advances.realtimerendering.com/destiny/i3d_2015/I3D_Tatarchuk_keynote_2015_for_web.pdf
			// http://advances.realtimerendering.com/s2013/Tatarchuk-Destiny-SIGGRAPH2013.pdf
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				int2 position = input.position.xy + 1;
				int2 offset = (((position & 3) >> 1) << 1) - 1; // same as position%4 < 2 ? -1 : 1; but faster

				float background_depth = linear_depth.Load(int3(input.position.xy, 0)).r;

				// TODO: is the result of d the same for both branches?
				#if defined(DX10_CAPS) || defined(RENDERER_GNM) || defined(PLATFORM_XB1) // Todo: Implement compile_options for consoles
					// TODO: optmize sample pattern
					float d0 = input_texture1.tex.Load(int3(input.position.xy/4, 0)).r;
					float d1 = input_texture1.tex.Load(int3(input.position.xy/4 + int2(offset.x, 0), 0)).r;
					float d2 = input_texture1.tex.Load(int3(input.position.xy/4 + int2(0, offset.y), 0)).r;
					float d3 = input_texture1.tex.Load(int3(input.position.xy/4 + offset, 0)).r;
					float4 d = float4(d0, d1, d2, d3);
				#else
					float4 d = input_texture1.tex.Gather(input_texture1.state, input.uv);
				#endif

				// Based on nearest-depth upsampling
				// https://mynameismjp.wordpress.com/2015/09/13/programmable-sample-points/
				float2 particle_depth_values = 0;
				half4 color = 0;
				[branch]
				if (all(d < background_depth)) {
					particle_depth_values =  TEX2D(input_texture1, input.uv).rg;
					color = TEX2D(input_texture0, input.uv);
				} else {
					particle_depth_values = input_texture1.tex.Load(int3(input.position.xy/4, 0)).rg;
					color = input_texture0.tex.Load(int3(input.position.xy/4, 0));
				}

				float depth_mean = particle_depth_values.x;
				float depth_variance = particle_depth_values.y - depth_mean*depth_mean;
				depth_variance = depth_variance > 0.0 ? sqrt(depth_variance) : 0.001;
				
				float diff = max(background_depth - depth_mean, 0.0);
				float C = 1.38629436112; // 2 * ln(2)
				float T = exp2(-diff*diff / (C * depth_variance));

				return color * (1.0 - T);
			}
		"""
	}

	depth_filter = {		
		includes = [ "common" ]
		samplers = {
		}

		code="""
			Texture2D<float4> input_texture0;
						
			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float2 inv_input_texture0_size;	
				float2 output_target_base_size;		
			CBUFFER_END
			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				return o;
			}			
			
			#if defined(GNM)
				#define DEPTH S_DEPTH_OUTPUT
			#endif

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float ps_main(PS_INPUT input) : DEPTH {
				#define load_depth(coordinate) input_texture0.Load(coordinate).r

				int2 coord = input.position.xy*4.0 + 0.5;
				float4 depth1 = float4(load_depth(int3(coord + int2(-2, -2), 0.0)).r, load_depth(int3(coord + int2(-1, -2), 0.0)).r, load_depth(int3(coord + int2(0, -2), 0.0)).r, load_depth(int3(coord + int2(1, -2), 0.0)).r);
				float4 depth2 = float4(load_depth(int3(coord + int2(-2, -1), 0.0)).r, load_depth(int3(coord + int2(-1, -1), 0.0)).r, load_depth(int3(coord + int2(0, -1), 0.0)).r, load_depth(int3(coord + int2(1, -1), 0.0)).r);
				float4 depth3 = float4(load_depth(int3(coord + int2(-2,  0), 0.0)).r, load_depth(int3(coord + int2(-1,  0), 0.0)).r, load_depth(int3(coord + int2(0,  0), 0.0)).r, load_depth(int3(coord + int2(1,  0), 0.0)).r);
				float4 depth4 = float4(load_depth(int3(coord + int2(-2,  1), 0.0)).r, load_depth(int3(coord + int2(-1,  1), 0.0)).r, load_depth(int3(coord + int2(0,  1), 0.0)).r, load_depth(int3(coord + int2(1,  1), 0.0)).r);

				#if defined(MIN_FILTER)
					float4 min_column = min(min(depth1, depth2), min(depth3, depth4));
					float depth = min(min(min_column.x, min_column.y), min(min_column.z, min_column.w));
				#elif defined(MAX_FILTER)
					float4 max_column = max(max(depth1, depth2), max(depth3, depth4));
					float depth = max(max(max_column.x, max_column.y), max(max_column.z, max_column.w));
				#else
					float depth = dot(depth1 + depth2 + depth3 + depth4, float4(0.0625, 0.0625, 0.0625, 0.0625));
				#endif

				return depth;
			}
		"""
	}

	init_linear_depth_div4 = {		
		includes = [ "common", "gbuffer_access" ]
		samplers = {
		}

		code="""
			Texture2D<float4> input_texture0;
						
			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
			};

			struct PS_OUTPUT {
				float4 linear_depth : SV_TARGET0;
				float4 linear_depth_squared : SV_TARGET1;
			};		 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float2 inv_input_texture0_size;	
				float2 output_target_base_size;		
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				return o;
			}

			#ifdef RENDERER_GNM
				#pragma PSSL_target_output_format (target 0 FMT_32_R)
			#endif			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_OUTPUT ps_main(PS_INPUT input) {
				float clip_depth = input_texture0.Load(int3(input.position.xy, 0));
				float depth = linearize_depth(clip_depth);

				PS_OUTPUT o;
				o.linear_depth = float4(depth, 0, 0, 0);
				// o.linear_depth_squared = float4(depth, depth * depth, 0, 0); // TODO: this coulde be interesting, however it results in bad edge artefacts atm.
				o.linear_depth_squared = float4(0, 0, 0, 0);
				return o;
			}
		"""
	}

	skin_debug = {
		includes = [ "common" ]
		samplers = {
		}

		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(input_texture1);

			Texture2D linear_depth;

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.uv = input.uv;

				return o;
			}

			// Upsampling method based on Destiny VDP
			// http://advances.realtimerendering.com/destiny/i3d_2015/I3D_Tatarchuk_keynote_2015_for_web.pdf
			// http://advances.realtimerendering.com/s2013/Tatarchuk-Destiny-SIGGRAPH2013.pdf
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			half4 ps_main(PS_INPUT input) : SV_TARGET0 {
				#if defined(SKIN)
					return float4(0, 1, 0, 0.8);
				#else
					return float4(0, 0, 0, 0.5);
				#endif
			}
		"""
	}

	fill_far_plane = {
		includes = [ "common" ]

		code="""
			struct VS_INPUT {
				float4 position : POSITION;
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
			};

			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);
				o.position.z = o.position.w;
				return o;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) : SV_TARGET0 {
				return float4(0, 0, 0, 0);
			}
		"""
	}

	rgba_temporal_aa = {		
		includes = [ "common", "gbuffer_access", "taa_offsets", "color_management", "post_processing_common" ]
		samplers = {
			input_texture0 = { sampler_states = "clamp_point" }
			input_texture1 = { sampler_states = "clamp_linear" }
			input_texture2 = { sampler_states = "clamp_point" }	
		}
		  
		code="""
			DECLARE_SAMPLER_2D(input_texture0);
			DECLARE_SAMPLER_2D(input_texture1);
			DECLARE_SAMPLER_2D(input_texture2);
			
			#define TAA_SIMPLE_BLEND_FACTOR 0.05

			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
			CBUFFER_END
			
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);				
				o.uv = input.uv;
				
				return o;
			}			

			float4 ps_main(PS_INPUT input) : SV_TARGET0 {
				// Current fragment generic info
				float4 result = 0;
				float2 pixel_size = 1.0/back_buffer_size;

				float2 uv = input.uv;
				float3 ss_pos = float3(uv, 0);
				float3 ss_front_most_neighbor = ss_pos;

				// Reprojection info
				float2 motion_vector = decode_velocity(TEX2D(input_texture2, ss_front_most_neighbor.xy).VELOCITY_COMPONENTS);
				float2 ss_prev_pos = ss_pos.xy - motion_vector;
				
				float3 prev_sample = TEX2D(input_texture1, ss_prev_pos).rgb;

				float2 nd_prev_pos = ss_prev_pos * 2.0 - 1.0;
				bool reprojection_is_offscreen = max(abs(nd_prev_pos.x), abs(nd_prev_pos.y)) >= 1.0;

				// 3x3 neighbors info
				int3 st = input.position;
				float4 reconstructed_sample = input_texture0.tex.Load(st, + neighbor_offsets[4]);

				float3 sample0 = input_texture0.tex.Load(st, + neighbor_offsets[0]).rgb;
				float3 sample1 = input_texture0.tex.Load(st, + neighbor_offsets[1]).rgb;
				float3 sample2 = input_texture0.tex.Load(st, + neighbor_offsets[2]).rgb;
				float3 sample3 = input_texture0.tex.Load(st, + neighbor_offsets[3]).rgb;
				float3 sample4 = reconstructed_sample.rgb;
				float3 sample5 = input_texture0.tex.Load(st, + neighbor_offsets[5]).rgb;
				float3 sample6 = input_texture0.tex.Load(st, + neighbor_offsets[6]).rgb;
				float3 sample7 = input_texture0.tex.Load(st, + neighbor_offsets[7]).rgb;
				float3 sample8 = input_texture0.tex.Load(st, + neighbor_offsets[8]).rgb;

				// Shaped Neighorhood clamp info
				// We want the clamped of the min/max values to appear filtered.
				// We split the samples into two "neighborhoods" and average
				// them together (Karis 2014)
				// ________________  ________________
				// Neighborhood '1'  Neighborhood '2'
				//      0 1 2             - 1 -
				//      3 4 5             3 4 5
				//      6 7 8             - 7 -
				float3 neighborhood_1_min = min(min(sample0, sample2), min(sample6, sample8));
				float3 neighborhood_1_max = max(max(sample0, sample2), max(sample6, sample8));
				float3 neighborhood_2_min = min(min(min(sample1, sample3), min(sample4, sample5)), sample7);
				float3 neighborhood_2_max = max(max(max(sample1, sample3), max(sample4, sample5)), sample7);
				neighborhood_1_min = min(neighborhood_1_min, neighborhood_2_min);
				neighborhood_1_max = max(neighborhood_1_max, neighborhood_2_max);

				prev_sample = clamp(prev_sample, neighborhood_1_min, neighborhood_1_max);
				result = reprojection_is_offscreen ? reconstructed_sample : float4(lerp(prev_sample, reconstructed_sample.rgb, TAA_SIMPLE_BLEND_FACTOR), reconstructed_sample.a);

				return result;
			}
		"""
	}

}

shaders = {
	error_debug = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="error_debug" render_states="filter" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}

	init_luminance_adaptation = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="init_luminance_adaptation" render_states="filter" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}

	average_luminance_feedback = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="average_luminance_feedback" render_states="filter" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}


	volume_height_fog = {
		editor_options = [
			{
				name="Fog Density"
				options = [
					{ name="Density Map" define="DENSITY_MAP" }
				]
			}
			{
				name="Special"
				options = [
					{ name="Flip Z" define="Z_INVERSION" }
					{ name="Soft Volume" define="SOFT_VOLUME" }
					{ name="Enable Camera Inside Volume" define="ENABLE_INSIDE" tooltip="Extra computations to make correct fog calculations when camera is inside the volume." }
				]
			}
		]

		contexts = {
			default = {
				passes = [
					{
						defined="LOW_RES"
						pass = [
							{ layer="hdr_transparent_low_res" hlsl_shader="volume_height_fog" render_states="opacity_volume" }
						]			
						fail = [
							{ layer="hdr_transparent" hlsl_shader="volume_height_fog" render_states="opacity_volume" }
						]
					}
				]
			}
		}

		compile = {
			default = [
				{ if: "on_renderer(D3D11, D3D12, GNM) && render_setting(low_res_transparency)" defines=["LOW_RES"] }
				{ defines="" platforms = "D3D11 D3D12 GNM"}
			]
		}
	}

	fog_plane = {
		editor_options = [
			{
				name="Quality"
				options = [
					{ name="High Resolution" define="HIGH_RESOLUTION" tool_tip="Fog planes are low resolution by default. If this flag is enabled the particle will be rendered in full resolution." }
				]
			}
			{
				name="Settings"
				options = [
					{ name="Inverted: Fog pixels in-front of plane" define="INVERTED" }
					{ name="Mask out skydome" define="MASK_OUT_SKYDOME" }	
					{ name="Ambient tint" define="AMBIENT_TINT" }		
				]
			}		
		]
		
		contexts = {
			default = {
				passes = [
					{
						defined="LOW_RES_ENABLED"
						pass = [
							//{
								//defined="HIGH_RESOLUTION"
								//pass = [
									{ layer="hdr_transparent" hlsl_shader="fog_plane" render_states="fog_plane" }
								//]
								//fail = [
								//	{ layer="hdr_transparent_low_res" hlsl_shader="fog_plane" defines=["LOW_RES"] render_states="fog_plane" }
								//]
							//}
						]			
						fail = [
							{ layer="hdr_transparent" hlsl_shader="fog_plane" render_states="fog_plane" }
						]
					}
				]
			}			
		}	
		
		compile = {
			default = [
				{ if: "on_renderer(D3D11, D3D12, GNM) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] }			
				{ defines="" platforms = "D3D11 D3D12 GNM"} 
			]
		} 
	} 	

	light_shafts = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="light_shafts" render_states="filter" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}

	skydome_billboard = {
		editor_advanced_mode = false
		editor_options = [
			{
				name="Sun Direction"
				options = [
					{ name="Use Secondary Sun" define="SECONDARY_SUN" }
				]
			}
		]

		contexts = {
			default = {
				passes = [
					{ layer="skydome_billboard" hlsl_shader="skydome_billboard" render_states="skydome_billboard" }
				]
			}

			// TODO: material transfer
		}

		compile = {
			default = [
				{ defines=[""] }
			]
		}
	}

	fixed_function_blend = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="fixed_function_blend" render_states="filter_blend" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	skin_filter = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="skin_filter" render_states="skin_filter" }
				]
			}
		}

		compile = {
			default = [
				{ defines="INDEPENDENT_BLEND" platforms="GNM" }
				{ defines="INDEPENDENT_BLEND" render_caps={ feature_level="DX11_0" } platforms="D3D11" }
				{ defines="INDEPENDENT_BLEND" render_caps={ feature_level="DX12_1" } platforms="D3D12" }
				{ defines="INDEPENDENT_BLEND DX10_1" render_caps={ feature_level="DX10_1" } platforms="D3D11" }
				{ defines="SEPARATE_BLEND" platforms="D3D11 D3D12 GNM" }
			]
		}
	}

	add_colored_outline = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="add_colored_outline" render_states="outline_stencil" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	specular_aa = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="specular_aa" render_states="filter_specular_aa" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	apply_hdr_transparent = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="apply_hdr_transparent" render_states="filter_hdr_transparency" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	depth_filter = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="depth_filter" render_states="filter_depth" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	skin_debug = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="skin_debug" render_states="skin_debug" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	init_linear_depth_div4 = {
		editor_advanced_mode = true

		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="init_linear_depth_div4" render_states="init_linear_depth_div4" }
				]
			}
		}

		compile = {
			default = [
				{ defines=[] }
			]
		}
	}

	fill_far_plane = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="fill_far_plane" render_states="filter_far" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}

	rgba_temporal_aa = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="rgba_temporal_aa" render_states="filter" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}
}

static_compile= [
	{ shader="error_debug" }
	{ shader="init_luminance_adaptation" }
	{ shader="average_luminance_feedback" }

	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" }
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" defines=["BRIGHT_PASS"] }
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" defines=["BRIGHT_PASS" "EYE_ADAPTATION"] }

	{ shader="fixed_function_blend" }
	{ shader="fixed_function_blend" defines=["POINT_SAMPLER"] }
	{ shader="fixed_function_blend" defines=["PREMULTIPLIED"] }
	{ shader="fixed_function_blend" defines=["PREMULTIPLIED" "OUTLINE"] }
	{ if: "!on_renderer(GL)" shader="fixed_function_blend" defines=["POINT_SAMPLER" "SKIN"] }

	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP0"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP0"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP1"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP1"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP2"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP2"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP0"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP1"] }
	{ if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP2"] }

	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" }
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" defines=["FILL"] }
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" defines=["OPAQUE"] }
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="specular_aa" defines=["FIRST_PASS"]}
	{ if: "on_renderer(D3D11, D3D12, GNM)" shader="specular_aa" defines=["SECOND_PASS"]}

	{ shader="apply_hdr_transparent" }

	{ shader="depth_filter" defines=["MIN_FILTER"] }
	{ shader="depth_filter" defines=["MAX_FILTER"] }
	{ shader="depth_filter" }

	{ shader="skin_debug" }
	{ shader="skin_debug" defines=["SKIN"] }

	{ shader="init_linear_depth_div4" }

	{ shader="fill_far_plane" }

	{ shader="rgba_temporal_aa" }
]
