/* =========================================================================== Copyright (C) 2023-2024 Gian 'myT' Schellenbaum This file is part of Challenge Quake 3 (CNQ3). Challenge Quake 3 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. Challenge Quake 3 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 Challenge Quake 3. If not, see . =========================================================================== */ // add fragments of transparent surfaces to per-pixel linked lists #include "common.hlsli" #include "alpha_test.h.hlsli" #include "oit.h.hlsli" #include "scene_view.h.hlsli" cbuffer RootConstants { // geometry matrix modelViewMatrix; // general uint textureIndex; uint samplerIndex; uint alphaTest; uint counterBuffer; uint indexTexture; uint fragmentBuffer; float greyscale; uint stateBits; uint shaderTrace; uint depthFadeDistOffset; // offset: fp16 - distance: fp16 uint depthFadeScaleBiasPO; // polygon offset: 1 - enable: 1 - color bias: 4 - color scale: 4 }; struct VIn { float3 position : POSITION; float3 normal : NORMAL; float2 texCoords : TEXCOORD0; float4 color : COLOR0; }; struct VOut { float4 position : SV_Position; float3 normal : NORMAL; float2 texCoords : TEXCOORD0; float4 color : COLOR0; float clipDist : SV_ClipDistance0; float2 proj2232 : PROJ; float depthVS : DEPTHVS; }; VOut vs(VIn input) { SceneView scene = GetSceneView(); float4 positionVS = mul(modelViewMatrix, float4(input.position.xyz, 1)); matrix projectionMatrix = scene.projectionMatrix; VOut output; output.position = mul(projectionMatrix, positionVS); output.normal = input.normal; output.texCoords = input.texCoords; output.color = input.color; output.clipDist = dot(positionVS, scene.clipPlane); output.proj2232 = float2(-projectionMatrix[2][2], projectionMatrix[2][3]); output.depthVS = -positionVS.z; return output; } bool IsFragmentUseless(uint blendBits, float4 color) { const float epsilon = 1.0 / 1024.0; if(blendBits == GLS_BLEND_ADDITIVE && all(color.rgb < epsilon.xxx)) { return true; } if((blendBits == GLS_BLEND_STD_ALPHA || blendBits == GLS_BLEND_PMUL_ALPHA) && color.a < epsilon) { return true; } if((blendBits == GLS_BLEND_FILTER || blendBits == GLS_BLEND_FILTER_V2) && all(color.rgb > (1.0 - epsilon).xxx) && all(color.rgb < (1.0 + epsilon).xxx)) { return true; } return false; } [earlydepthstencil] void ps(VOut input) { Texture2D texture0 = ResourceDescriptorHeap[textureIndex]; SamplerState sampler0 = SamplerDescriptorHeap[samplerIndex]; float4 dst = texture0.Sample(sampler0, input.texCoords) * input.color; if(FailsAlphaTest(dst.a, alphaTest)) { return; } dst = MakeGreyscale(dst, greyscale); uint blendBits = stateBits & GLS_BLEND_BITS; if(IsFragmentUseless(blendBits, dst)) { return; } RWStructuredBuffer counter = ResourceDescriptorHeap[counterBuffer]; uint fragmentIndex; InterlockedAdd(counter[0].fragmentCount, 1, fragmentIndex); if(fragmentIndex < counter[0].maxFragmentCount) { RWTexture2D indexTex = ResourceDescriptorHeap[indexTexture]; RWStructuredBuffer fragments = ResourceDescriptorHeap[fragmentBuffer]; uint prevFragmentIndex; InterlockedExchange(indexTex[int2(input.position.xy)], fragmentIndex, prevFragmentIndex); OIT_Fragment fragment; fragment.color = PackColor(dst); fragment.depth = input.depthVS; fragment.stateBits = stateBits; fragment.next = prevFragmentIndex; fragment.shaderTrace = shaderTrace; fragment.depthFadeDistOffset = depthFadeDistOffset; fragment.depthFadeScaleBiasPO = depthFadeScaleBiasPO; fragments[fragmentIndex] = fragment; } else { uint garbage; InterlockedAdd(counter[0].overflowCount, 1, garbage); InterlockedAdd(counter[0].fragmentCount, -1, garbage); } }