/* Created on: Nov 6, 2019 Copyright 2019 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "oit_shaders.h" #include "compiler.h" static const char OITVertexShaderSource[] = R"( #version 450 #define pp_Gouraud %d #if pp_Gouraud == 0 #define INTERPOLATION flat #else #define INTERPOLATION smooth #endif layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms { mat4 normal_matrix; } uniformBuffer; layout (location = 0) in vec4 in_pos; layout (location = 1) in uvec4 in_base; layout (location = 2) in uvec4 in_offs; layout (location = 3) in mediump vec2 in_uv; layout (location = 4) in uvec4 in_base1; // New for OIT, only for OP/PT with 2-volume layout (location = 5) in uvec4 in_offs1; layout (location = 6) in mediump vec2 in_uv1; layout (location = 0) INTERPOLATION out lowp vec4 vtx_base; layout (location = 1) INTERPOLATION out lowp vec4 vtx_offs; layout (location = 2) out mediump vec2 vtx_uv; layout (location = 3) INTERPOLATION out lowp vec4 vtx_base1; // New for OIT, only for OP/PT with 2-volume layout (location = 4) INTERPOLATION out lowp vec4 vtx_offs1; layout (location = 5) out mediump vec2 vtx_uv1; void main() { vtx_base = vec4(in_base) / 255.0; vtx_offs = vec4(in_offs) / 255.0; vtx_uv = in_uv; vtx_base1 = vec4(in_base1) / 255.0; // New for OIT, only for OP/PT with 2-volume vtx_offs1 = vec4(in_offs1) / 255.0; vtx_uv1 = in_uv1; vec4 vpos = in_pos; if (vpos.z < 0.0 || vpos.z > 3.4e37) { gl_Position = vec4(0.0, 0.0, 1.0, 1.0 / vpos.z); return; } vpos = uniformBuffer.normal_matrix * vpos; vpos.w = 1.0 / vpos.z; vpos.z = vpos.w; vpos.xy *= vpos.w; gl_Position = vpos; } )"; static const char OITShaderHeader[] = R"( #version 450 layout (std140, set = 0, binding = 1) uniform FragmentShaderUniforms { vec4 colorClampMin; vec4 colorClampMax; vec4 sp_FOG_COL_RAM; vec4 sp_FOG_COL_VERT; float cp_AlphaTestValue; float sp_FOG_DENSITY; float shade_scale_factor; } uniformBuffer; layout(set = 3, binding = 2, r32ui) uniform coherent restrict uimage2D abufferPointerImg; struct Pixel { vec4 color; float depth; uint seq_num; uint next; }; #define EOL 0xFFFFFFFFu layout (set = 3, binding = 0, std430) coherent restrict buffer PixelBuffer_ { Pixel pixels[]; } PixelBuffer; layout(set = 3, binding = 1) buffer PixelCounter_ { uint buffer_index; } PixelCounter; #define ZERO 0 #define ONE 1 #define OTHER_COLOR 2 #define INVERSE_OTHER_COLOR 3 #define SRC_ALPHA 4 #define INVERSE_SRC_ALPHA 5 #define DST_ALPHA 6 #define INVERSE_DST_ALPHA 7 uint getNextPixelIndex() { uint index = atomicAdd(PixelCounter.buffer_index, 1); if (index >= PixelBuffer.pixels.length()) // Buffer overflow discard; return index; } void setFragDepth(void) { float w = 100000.0 * gl_FragCoord.w; gl_FragDepth = log2(1.0 + w) / 34.0; } struct PolyParam { int first; int count; int texid_low; int texid_high; int tsp; int tcw; int pcw; int isp; float zvZ; int tileclip; int tsp1; int tcw1; int texid1_low; int texid1_high; }; layout (set = 0, binding = 3, std430) readonly buffer TrPolyParamBuffer { PolyParam tr_poly_params[]; } TrPolyParam; #define GET_TSP_FOR_AREA int tsp = area1 ? pp.tsp1 : pp.tsp; int getSrcBlendFunc(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return (tsp >> 29) & 7; } int getDstBlendFunc(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return (tsp >> 26) & 7; } bool getSrcSelect(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return ((tsp >> 25) & 1) != 0; } bool getDstSelect(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return ((tsp >> 24) & 1) != 0; } int getFogControl(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return (tsp >> 22) & 3; } bool getUseAlpha(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return ((tsp >> 20) & 1) != 0; } bool getIgnoreTexAlpha(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return ((tsp >> 19) & 1) != 0; } int getShadingInstruction(const PolyParam pp, bool area1) { GET_TSP_FOR_AREA return (tsp >> 6) & 3; } int getDepthFunc(const PolyParam pp) { return (pp.isp >> 29) & 7; } bool getDepthMask(const PolyParam pp) { return ((pp.isp >> 26) & 1) != 1; } bool getShadowEnable(const PolyParam pp) { return ((pp.pcw >> 7) & 1) != 0; } uint getPolyNumber(const Pixel pixel) { return pixel.seq_num & 0x3FFFFFFFu; } #define SHADOW_STENCIL 0x40000000u #define SHADOW_ACC 0x80000000u bool isShadowed(const Pixel pixel) { return (pixel.seq_num & SHADOW_ACC) == SHADOW_ACC; } bool isTwoVolumes(const PolyParam pp) { return pp.tsp1 != -1 || pp.tcw1 != -1; } )"; static const char OITFragmentShaderSource[] = R"( #define cp_AlphaTest %d #define pp_ClipInside %d #define pp_UseAlpha %d #define pp_Texture %d #define pp_IgnoreTexA %d #define pp_ShadInstr %d #define pp_Offset %d #define pp_FogCtrl %d #define pp_TwoVolumes %d #define pp_DepthFunc %d #define pp_Gouraud %d #define pp_BumpMap %d #define ColorClamping %d #define PASS %d #define PI 3.1415926 #if PASS <= 1 layout (location = 0) out vec4 FragColor; #define gl_FragColor FragColor #endif #if pp_TwoVolumes == 1 #define IF(x) if (x) #else #define IF(x) #endif #if pp_Gouraud == 0 #define INTERPOLATION flat #else #define INTERPOLATION smooth #endif layout (push_constant) uniform pushBlock { vec4 clipTest; ivec4 blend_mode0; float trilinearAlpha; int pp_Number; // two volume mode ivec4 blend_mode1; int shading_instr0; int shading_instr1; int fog_control0; int fog_control1; int use_alpha0; int use_alpha1; int ignore_tex_alpha0; int ignore_tex_alpha1; } pushConstants; #if pp_Texture == 1 layout (set = 1, binding = 0) uniform sampler2D tex0; #if pp_TwoVolumes == 1 layout (set = 1, binding = 1) uniform sampler2D tex1; #endif #endif #if PASS == 1 layout (input_attachment_index = 0, set = 0, binding = 4) uniform usubpassInput shadow_stencil; #endif #if PASS == 3 layout (input_attachment_index = 0, set = 0, binding = 5) uniform subpassInput DepthTex; #endif // Vertex input layout (location = 0) INTERPOLATION in lowp vec4 vtx_base; layout (location = 1) INTERPOLATION in lowp vec4 vtx_offs; layout (location = 2) in mediump vec2 vtx_uv; layout (location = 3) INTERPOLATION in lowp vec4 vtx_base1; // new for OIT. Only if 2 vol layout (location = 4) INTERPOLATION in lowp vec4 vtx_offs1; layout (location = 5) in mediump vec2 vtx_uv1; #if pp_FogCtrl != 2 layout (set = 0, binding = 2) uniform sampler2D fog_table; float fog_mode2(float w) { float z = clamp(w * uniformBuffer.sp_FOG_DENSITY, 1.0, 255.9999); float exp = floor(log2(z)); float m = z * 16.0 / pow(2.0, exp) - 16.0; float idx = floor(m) + exp * 16.0 + 0.5; vec4 fog_coef = texture(fog_table, vec2(idx / 128.0, 0.75 - (m - floor(m)) / 2.0)); return fog_coef.r; } #endif vec4 colorClamp(vec4 col) { // TODO This can change in two-volume mode #if ColorClamping == 1 return clamp(col, uniformBuffer.colorClampMin, uniformBuffer.colorClampMax); #else return col; #endif } void main() { setFragDepth(); #if PASS == 3 // Manual depth testing highp float frontDepth = subpassLoad(DepthTex).r; #if pp_DepthFunc == 0 // Never discard; #elif pp_DepthFunc == 1 // Less if (gl_FragDepth >= frontDepth) discard; #elif pp_DepthFunc == 2 // Equal if (gl_FragDepth != frontDepth) discard; #elif pp_DepthFunc == 3 // Less or equal if (gl_FragDepth > frontDepth) discard; #elif pp_DepthFunc == 4 // Greater if (gl_FragDepth <= frontDepth) discard; #elif pp_DepthFunc == 5 // Not equal if (gl_FragDepth == frontDepth) discard; #elif pp_DepthFunc == 6 // Greater or equal if (gl_FragDepth < frontDepth) discard; #endif #endif // Clip inside the box #if pp_ClipInside == 1 if (gl_FragCoord.x >= pushConstants.clipTest.x && gl_FragCoord.x <= pushConstants.clipTest.z && gl_FragCoord.y >= pushConstants.clipTest.y && gl_FragCoord.y <= pushConstants.clipTest.w) discard; #endif highp vec4 color = vtx_base; lowp vec4 offset = vtx_offs; mediump vec2 uv = vtx_uv; bool area1 = false; ivec2 cur_blend_mode = pushConstants.blend_mode0.xy; #if pp_TwoVolumes == 1 bool cur_use_alpha = pushConstants.use_alpha0 != 0; bool cur_ignore_tex_alpha = pushConstants.ignore_tex_alpha0 != 0; int cur_shading_instr = pushConstants.shading_instr0; int cur_fog_control = pushConstants.fog_control0; #if PASS == 1 uvec4 stencil = subpassLoad(shadow_stencil); if (stencil.r == 0x81u) { color = vtx_base1; offset = vtx_offs1; uv = vtx_uv1; area1 = true; cur_blend_mode = pushConstants.blend_mode1.xy; cur_use_alpha = pushConstants.use_alpha1 != 0; cur_ignore_tex_alpha = pushConstants.ignore_tex_alpha1 != 0; cur_shading_instr = pushConstants.shading_instr1; cur_fog_control = pushConstants.fog_control1; } #endif #endif #if pp_UseAlpha == 0 || pp_TwoVolumes == 1 IF (!cur_use_alpha) color.a = 1.0; #endif #if pp_FogCtrl == 3 || pp_TwoVolumes == 1 // LUT Mode 2 IF (cur_fog_control == 3) color = vec4(uniformBuffer.sp_FOG_COL_RAM.rgb, fog_mode2(gl_FragCoord.w)); #endif #if pp_Texture==1 { highp vec4 texcol; #if pp_TwoVolumes == 1 if (area1) texcol = texture(tex1, uv); else #endif texcol = texture(tex0, uv); #if pp_BumpMap == 1 highp float s = PI / 2.0 * (texcol.a * 15.0 * 16.0 + texcol.r * 15.0) / 255.0; highp float r = 2.0 * PI * (texcol.g * 15.0 * 16.0 + texcol.b * 15.0) / 255.0; texcol.a = clamp(vtx_offs.a + vtx_offs.r * sin(s) + vtx_offs.g * cos(s) * cos(r - 2.0 * PI * vtx_offs.b), 0.0, 1.0); texcol.rgb = vec3(1.0, 1.0, 1.0); #else #if pp_IgnoreTexA==1 || pp_TwoVolumes == 1 IF(cur_ignore_tex_alpha) texcol.a = 1.0; #endif #if cp_AlphaTest == 1 if (uniformBuffer.cp_AlphaTestValue > texcol.a) discard; #endif #endif #if pp_ShadInstr == 0 || pp_TwoVolumes == 1 // DECAL IF(cur_shading_instr == 0) { color = texcol; } #endif #if pp_ShadInstr == 1 || pp_TwoVolumes == 1 // MODULATE IF(cur_shading_instr == 1) { color.rgb *= texcol.rgb; color.a = texcol.a; } #endif #if pp_ShadInstr == 2 || pp_TwoVolumes == 1 // DECAL ALPHA IF(cur_shading_instr == 2) { color.rgb = mix(color.rgb, texcol.rgb, texcol.a); } #endif #if pp_ShadInstr == 3 || pp_TwoVolumes == 1 // MODULATE ALPHA IF(cur_shading_instr == 3) { color *= texcol; } #endif #if pp_Offset == 1 && pp_BumpMap == 0 { color.rgb += offset.rgb; } #endif } #endif #if PASS == 1 && pp_TwoVolumes == 0 uvec4 stencil = subpassLoad(shadow_stencil); if (stencil.r == 0x81u) color.rgb *= uniformBuffer.shade_scale_factor; #endif color = colorClamp(color); #if pp_FogCtrl == 0 || pp_TwoVolumes == 1 // LUT IF(cur_fog_control == 0) { color.rgb = mix(color.rgb, uniformBuffer.sp_FOG_COL_RAM.rgb, fog_mode2(gl_FragCoord.w)); } #endif #if pp_Offset==1 && pp_BumpMap == 0 && (pp_FogCtrl == 1 || pp_TwoVolumes == 1) // Per vertex IF(cur_fog_control == 1) { color.rgb = mix(color.rgb, uniformBuffer.sp_FOG_COL_VERT.rgb, offset.a); } #endif color *= pushConstants.trilinearAlpha; #if cp_AlphaTest == 1 color.a = 1.0; #endif //color.rgb=vec3(gl_FragCoord.w * uniformBuffer.sp_FOG_DENSITY / 128.0); #if PASS == 1 FragColor = color; #elif PASS > 1 // Discard as many pixels as possible switch (cur_blend_mode.y) // DST { case ONE: switch (cur_blend_mode.x) // SRC { case ZERO: discard; case ONE: case OTHER_COLOR: case INVERSE_OTHER_COLOR: if (color == vec4(0.0)) discard; break; case SRC_ALPHA: if (color.a == 0.0 || color.rgb == vec3(0.0)) discard; break; case INVERSE_SRC_ALPHA: if (color.a == 1.0 || color.rgb == vec3(0.0)) discard; break; } break; case OTHER_COLOR: if (cur_blend_mode.x == ZERO && color == vec4(1.0)) discard; break; case INVERSE_OTHER_COLOR: if (cur_blend_mode.x <= SRC_ALPHA && color == vec4(0.0)) discard; break; case SRC_ALPHA: if ((cur_blend_mode.x == ZERO || cur_blend_mode.x == INVERSE_SRC_ALPHA) && color.a == 1.0) discard; break; case INVERSE_SRC_ALPHA: switch (cur_blend_mode.x) // SRC { case ZERO: case SRC_ALPHA: if (color.a == 0.0) discard; break; case ONE: case OTHER_COLOR: case INVERSE_OTHER_COLOR: if (color == vec4(0.0)) discard; break; } break; } ivec2 coords = ivec2(gl_FragCoord.xy); uint idx = getNextPixelIndex(); Pixel pixel; pixel.color = color; pixel.depth = gl_FragDepth; pixel.seq_num = uint(pushConstants.pp_Number); pixel.next = imageAtomicExchange(abufferPointerImg, coords, idx); PixelBuffer.pixels[idx] = pixel; #endif } )"; // FIXME duplicate of non-oit static const char OITModVolVertexShaderSource[] = R"( #version 450 layout (std140, set = 0, binding = 0) uniform VertexShaderUniforms { mat4 normal_matrix; } uniformBuffer; layout (location = 0) in vec4 in_pos; void main() { vec4 vpos = in_pos; if (vpos.z < 0.0 || vpos.z > 3.4e37) { gl_Position = vec4(0.0, 0.0, 1.0, 1.0 / vpos.z); return; } vpos = uniformBuffer.normal_matrix * vpos; vpos.w = 1.0 / vpos.z; vpos.z = vpos.w; vpos.xy *= vpos.w; gl_Position = vpos; } )"; static const char OITModifierVolumeShader[] = R"( void main() { setFragDepth(); } )"; #define MAX_PIXELS_PER_FRAGMENT "32" static const char OITFinalShaderSource[] = "#define MAX_PIXELS_PER_FRAGMENT " MAX_PIXELS_PER_FRAGMENT R"( #define DEPTH_SORTED %d layout (input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput tex; layout (location = 0) out vec4 FragColor; uint pixel_list[MAX_PIXELS_PER_FRAGMENT]; int fillAndSortFragmentArray(ivec2 coords) { // Load fragments into a local memory array for sorting uint idx = imageLoad(abufferPointerImg, coords).x; int count = 0; for (; idx != EOL && count < MAX_PIXELS_PER_FRAGMENT; count++) { const Pixel p = PixelBuffer.pixels[idx]; int j = count - 1; Pixel jp = PixelBuffer.pixels[pixel_list[j]]; #if DEPTH_SORTED == 1 while (j >= 0 && (jp.depth > p.depth || (jp.depth == p.depth && getPolyNumber(jp) > getPolyNumber(p)))) #else while (j >= 0 && getPolyNumber(jp) > getPolyNumber(p)) #endif { pixel_list[j + 1] = pixel_list[j]; j--; jp = PixelBuffer.pixels[pixel_list[j]]; } pixel_list[j + 1] = idx; idx = p.next; } return count; } // Blend fragments back-to-front vec4 resolveAlphaBlend(ivec2 coords) { // Copy and sort fragments into a local array int num_frag = fillAndSortFragmentArray(coords); vec4 finalColor = subpassLoad(tex); vec4 secondaryBuffer = vec4(0.0); // Secondary accumulation buffer float depth = 1.0; bool do_depth_test = false; for (int i = 0; i < num_frag; i++) { const Pixel pixel = PixelBuffer.pixels[pixel_list[i]]; const PolyParam pp = TrPolyParam.tr_poly_params[getPolyNumber(pixel)]; #if DEPTH_SORTED != 1 const float frag_depth = pixel.depth; if (do_depth_test) { switch (getDepthFunc(pp)) { case 0: // Never continue; case 1: // Less if (frag_depth >= depth) continue; break; case 2: // Equal if (frag_depth != depth) continue; break; case 3: // Less or equal if (frag_depth > depth) continue; break; case 4: // Greater if (frag_depth <= depth) continue; break; case 5: // Not equal if (frag_depth == depth) continue; break; case 6: // Greater or equal if (frag_depth < depth) continue; break; case 7: // Always break; } } if (getDepthMask(pp)) { depth = frag_depth; do_depth_test = true; } #endif bool area1 = false; bool shadowed = false; if (isShadowed(pixel)) { if (isTwoVolumes(pp)) area1 = true; else shadowed = true; } vec4 srcColor; if (getSrcSelect(pp, area1)) srcColor = secondaryBuffer; else { srcColor = pixel.color; if (shadowed) srcColor.rgb *= uniformBuffer.shade_scale_factor; } vec4 dstColor = getDstSelect(pp, area1) ? secondaryBuffer : finalColor; vec4 srcCoef; vec4 dstCoef; int srcBlend = getSrcBlendFunc(pp, area1); switch (srcBlend) { case ZERO: srcCoef = vec4(0.0); break; case ONE: srcCoef = vec4(1.0); break; case OTHER_COLOR: srcCoef = finalColor; break; case INVERSE_OTHER_COLOR: srcCoef = vec4(1.0) - dstColor; break; case SRC_ALPHA: srcCoef = vec4(srcColor.a); break; case INVERSE_SRC_ALPHA: srcCoef = vec4(1.0 - srcColor.a); break; case DST_ALPHA: srcCoef = vec4(dstColor.a); break; case INVERSE_DST_ALPHA: srcCoef = vec4(1.0 - dstColor.a); break; } int dstBlend = getDstBlendFunc(pp, area1); switch (dstBlend) { case ZERO: dstCoef = vec4(0.0); break; case ONE: dstCoef = vec4(1.0); break; case OTHER_COLOR: dstCoef = srcColor; break; case INVERSE_OTHER_COLOR: dstCoef = vec4(1.0) - srcColor; break; case SRC_ALPHA: dstCoef = vec4(srcColor.a); break; case INVERSE_SRC_ALPHA: dstCoef = vec4(1.0 - srcColor.a); break; case DST_ALPHA: dstCoef = vec4(dstColor.a); break; case INVERSE_DST_ALPHA: dstCoef = vec4(1.0 - dstColor.a); break; } const vec4 result = clamp(dstColor * dstCoef + srcColor * srcCoef, 0.0, 1.0); if (getDstSelect(pp, area1)) secondaryBuffer = result; else finalColor = result; } return finalColor; } void main(void) { ivec2 coords = ivec2(gl_FragCoord.xy); // Compute and output final color for the frame buffer // Visualize the number of layers in use //FragColor = vec4(float(fillAndSortFragmentArray(coords)) / MAX_PIXELS_PER_FRAGMENT * 4, 0, 0, 1); FragColor = resolveAlphaBlend(coords); } )"; static const char OITClearShaderSource[] = R"( void main(void) { ivec2 coords = ivec2(gl_FragCoord.xy); // Reset pointers imageStore(abufferPointerImg, coords, uvec4(EOL)); } )"; static const char OITTranslucentModvolShaderSource[] = "#define MAX_PIXELS_PER_FRAGMENT " MAX_PIXELS_PER_FRAGMENT R"( #define MV_MODE %d // Must match ModifierVolumeMode enum values #define MV_XOR 0 #define MV_OR 1 #define MV_INCLUSION 2 #define MV_EXCLUSION 3 void main() { #if MV_MODE == MV_XOR || MV_MODE == MV_OR setFragDepth(); #endif ivec2 coords = ivec2(gl_FragCoord.xy); uint idx = imageLoad(abufferPointerImg, coords).x; int list_len = 0; while (idx != EOL && list_len < MAX_PIXELS_PER_FRAGMENT) { const Pixel pixel = PixelBuffer.pixels[idx]; const PolyParam pp = TrPolyParam.tr_poly_params[getPolyNumber(pixel)]; if (getShadowEnable(pp)) { #if MV_MODE == MV_XOR if (gl_FragDepth >= pixel.depth) atomicXor(PixelBuffer.pixels[idx].seq_num, SHADOW_STENCIL); #elif MV_MODE == MV_OR if (gl_FragDepth >= pixel.depth) atomicOr(PixelBuffer.pixels[idx].seq_num, SHADOW_STENCIL); #elif MV_MODE == MV_INCLUSION uint prev_val = atomicAnd(PixelBuffer.pixels[idx].seq_num, ~(SHADOW_STENCIL)); if ((prev_val & (SHADOW_STENCIL|SHADOW_ACC)) == SHADOW_STENCIL) PixelBuffer.pixels[idx].seq_num = bitfieldInsert(pixel.seq_num, 1u, 31, 1); #elif MV_MODE == MV_EXCLUSION uint prev_val = atomicAnd(PixelBuffer.pixels[idx].seq_num, ~(SHADOW_STENCIL|SHADOW_ACC)); if ((prev_val & (SHADOW_STENCIL|SHADOW_ACC)) == SHADOW_ACC) PixelBuffer.pixels[idx].seq_num = bitfieldInsert(pixel.seq_num, 1u, 31, 1); #endif } idx = pixel.next; list_len++; } } )"; static const char OITFinalVertexShaderSource[] = R"( #version 430 layout (location = 0) in vec3 in_pos; void main() { gl_Position = vec4(in_pos, 1.0); } )"; vk::UniqueShaderModule OITShaderManager::compileShader(const VertexShaderParams& params) { char buf[sizeof(OITVertexShaderSource) * 2]; sprintf(buf, OITVertexShaderSource, (int)params.gouraud); return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, buf); } vk::UniqueShaderModule OITShaderManager::compileShader(const FragmentShaderParams& params) { char buf[(sizeof(OITShaderHeader) + sizeof(OITFragmentShaderSource)) * 2]; strcpy(buf, OITShaderHeader); sprintf(buf + strlen(buf), OITFragmentShaderSource, (int)params.alphaTest, (int)params.insideClipTest, (int)params.useAlpha, (int)params.texture, (int)params.ignoreTexAlpha, params.shaderInstr, (int)params.offset, params.fog, (int)params.twoVolume, params.depthFunc, (int)params.gouraud, (int)params.bumpmap, (int)params.clamping, params.pass); return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf); } vk::UniqueShaderModule OITShaderManager::compileFinalShader(bool autosort) { char buf[(sizeof(OITShaderHeader) + sizeof(OITFinalShaderSource)) * 2]; strcpy(buf, OITShaderHeader); sprintf(buf + strlen(buf), OITFinalShaderSource, (int)autosort); return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf); } vk::UniqueShaderModule OITShaderManager::compileFinalVertexShader() { return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, OITFinalVertexShaderSource); } vk::UniqueShaderModule OITShaderManager::compileClearShader() { std::string source = OITShaderHeader; source += OITClearShaderSource; return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, source); } vk::UniqueShaderModule OITShaderManager::compileModVolVertexShader() { return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, OITModVolVertexShaderSource); } vk::UniqueShaderModule OITShaderManager::compileModVolFragmentShader() { std::string source = OITShaderHeader; source += OITModifierVolumeShader; return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, source); } void OITShaderManager::compileTrModVolFragmentShader(ModVolMode mode) { if (trModVolShaders.empty()) trModVolShaders.resize((size_t)ModVolMode::Final); char buf[(sizeof(OITShaderHeader) + sizeof(OITTranslucentModvolShaderSource)) * 2]; strcpy(buf, OITShaderHeader); sprintf(buf + strlen(buf), OITTranslucentModvolShaderSource, (int)mode); trModVolShaders[(size_t)mode] = ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, buf); }