/
redgpu
/
ezEngine
Обзор
Документация
Войти
/
redgpu
/
ezEngine
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
Data/Plugins/ParticlePlugin/Shaders/Particles/ParticleCommonVS.h
254 строки
9 KB
Jan Krassnigg
Particle System Improvements (#1995)
21 июл 2026, 16:50
Не верифицирован
21 июл 2026, 16:50
5e8aa52
Код
Авторство
О чём код?
#pragma once #define SHADOW_FORCE_LAST_CASCADE #include <Shaders/Common/GlobalConstants.h> #include <Shaders/Materials/MaterialInterpolator.h> #include <Shaders/Particles/ParticleSystemConstants.h> #if SHADING_QUALITY == SHADING_QUALITY_NORMAL # include <Shaders/Common/Lighting.h> #elif SHADING_QUALITY == SHADING_QUALITY_SIMPLIFIED # include <Shaders/Common/LightingSimplified.h> #else # error "Unknown shading quality configuration." #endif static float2 QuadPosOffsets[6] = { float2(0.0, 0.0), float2(1.0, 0.0), float2(1.0, 1.0), float2(0.0, 0.0), float2(1.0, 1.0), float2(0.0, 1.0), }; static float2 QuadTexCoordsBillboard[6] = { float2(1.0, 0.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0), float2(0.0, 1.0), float2(0.0, 0.0), }; static float2 QuadTexCoordsAxisAligned[6] = { float2(0.0, 1.0), float2(1.0, 1.0), float2(1.0, 0.0), float2(0.0, 1.0), float2(1.0, 0.0), float2(0.0, 0.0), }; uint CalcQuadParticleDataIndex(uint VertexID) { return VertexID / 6; } uint CalcQuadParticleVertexIndex(uint VertexID) { return VertexID % 6; } struct Quad { float4 worldPosition; float4 screenPosition; float3 normal; }; Quad CalcQuadOutputPositionWithTangents(uint vertexIndex, float3 inPosition, float3 inTangentX, float3 inTangentZ, float inSize) { float3 offsetRight = inTangentX * ((QuadPosOffsets[vertexIndex].x - 0.5) * inSize); float3 offsetUp = inTangentZ * ((QuadPosOffsets[vertexIndex].y - 0.5) * -inSize); Quad quad; quad.worldPosition = mul(ObjectToWorldMatrix, float4(inPosition + offsetRight + offsetUp, 1)); quad.screenPosition = mul(GetWorldToScreenMatrix(), quad.worldPosition); float3 centerNormal = normalize(mul((float3x3)ObjectToWorldMatrix, cross(inTangentZ, inTangentX))); float3 cornerNormal = normalize(quad.worldPosition.xyz - inPosition); quad.normal = normalize(lerp(centerNormal, cornerNormal, NormalCurvature)); return quad; } Quad CalcQuadOutputPositionWithAlignedAxis(uint vertexIndex, float3 inPosition, float3 inTangentX, float3 inTangentZ, float inSize) { float stretch = -inTangentZ.x; float3 inTangentXws = mul((float3x3)ObjectToWorldMatrix, inTangentX); float3 axisDir = normalize(inTangentXws); float3 orthoDir = normalize(cross(inTangentXws, GetCameraDirForwards())); float3 offsetRight = orthoDir * ((QuadPosOffsets[vertexIndex].x - 0.5) * inSize); float3 offsetUp = axisDir * (QuadPosOffsets[vertexIndex].y * inSize * -stretch); Quad quad; quad.worldPosition = mul(ObjectToWorldMatrix, float4(inPosition, 1)); quad.worldPosition.xyz += offsetRight + offsetUp; quad.screenPosition = mul(GetWorldToScreenMatrix(), quad.worldPosition); float3 centerNormal = cross(axisDir, orthoDir); float3 cornerNormal = normalize(offsetRight); quad.normal = normalize(lerp(centerNormal, cornerNormal, NormalCurvature)); return quad; } Quad CalcQuadOutputPositionAsBillboard(uint vertexIndex, float3 inPosition, float rotationOffset, float rotationSpeed, float inSize) { float2 angles; sincos(rotationOffset + rotationSpeed * TotalEffectLifeTime, angles.x, angles.y); float2x2 rotation = {angles.x, -angles.y, angles.y, angles.x}; float2 quadCorners = mul(rotation, QuadPosOffsets[vertexIndex] - 0.5); float3 offsetRight = GetCameraDirRight() * (quadCorners.x * inSize); float3 offsetUp = GetCameraDirUp() * (quadCorners.y * -inSize); Quad quad; quad.worldPosition = mul(ObjectToWorldMatrix, float4(inPosition, 1)) + float4(offsetRight + offsetUp, 0); quad.screenPosition = mul(GetWorldToScreenMatrix(), quad.worldPosition); float3 cornerNormal = normalize(quad.worldPosition.xyz - inPosition); quad.normal = normalize(lerp(-GetCameraDirForwards(), cornerNormal, NormalCurvature)); return quad; } float4 ComputeTextureAtlasRect(uint numVarsX, uint numVarsY, float varLerp, float4 texCoordOffsetAndSize) { if (numVarsX > 1 || numVarsY > 1) { uint numVars = numVarsX * numVarsY; uint idxVar = (uint)(numVars * varLerp); uint varY = idxVar / numVarsX; uint varX = (idxVar - (varY * numVarsX)); texCoordOffsetAndSize.zw = texCoordOffsetAndSize.zw / float2(numVarsX, numVarsY); texCoordOffsetAndSize.xy = texCoordOffsetAndSize.xy + texCoordOffsetAndSize.zw * float2(varX, varY); } return texCoordOffsetAndSize; } float4 ComputeAtlasRectRandomAnimated(uint numVarsX, uint numVarsY, float varLerp, uint numAnimsX, uint numAnimsY, float animLerp) { float4 texCoordOffsetAndSize = float4(0, 0, 1, 1); texCoordOffsetAndSize = ComputeTextureAtlasRect(numVarsX, numVarsY, varLerp, texCoordOffsetAndSize); texCoordOffsetAndSize = ComputeTextureAtlasRect(numAnimsX, numAnimsY, animLerp, texCoordOffsetAndSize); return texCoordOffsetAndSize; } float2 ComputeAtlasTexCoordRandomAnimated(float2 baseTexCoord, uint numVarsX, uint numVarsY, float varLerp, uint numAnimsX, uint numAnimsY, float animLerp) { float4 texCoordOffsetAndSize = ComputeAtlasRectRandomAnimated(numVarsX, numVarsY, varLerp, numAnimsX, numAnimsY, animLerp); return texCoordOffsetAndSize.xy + baseTexCoord * texCoordOffsetAndSize.zw; } // Rotates a normalized [0-1]^2 local UV (e.g. within one atlas cell) around its center. // orientation: 0 = Up (identity), 1 = Right (90° CW), 2 = Down (180°), 3 = Left (270° CW) // Lets a texture authored with its "forward" content running in any of the 4 cardinal // directions be used correctly, regardless of which direction the particle stretches/moves in. float2 RotateAtlasCellUV(float2 uv, uint orientation) { float2 c = uv - 0.5; float2 r = c; if (orientation == 1) r = float2(c.y, -c.x); // Right, 90° else if (orientation == 2) r = float2(-c.x, -c.y); // Down, 180° else if (orientation == 3) r = float2(-c.y, c.x); // Left, 270° return r + 0.5; } float3 CalculateParticleLighting(float4 screenPosition, float3 worldPosition, float3 worldNormal) { float3 totalLight = 0.0f; float3 indirectLightModulation = 1.0f; #if SHADING_QUALITY == SHADING_QUALITY_NORMAL float2 normalizedScreenPos = (screenPosition.xy / screenPosition.w) * float2(0.5, -0.5) + 0.5; float3 screenPos = float3(normalizedScreenPos * ViewportSize.xy, screenPosition.w); ezPerClusterData clusterData = GetClusterData(screenPos); float3 viewVector = normalize(GetCameraPosition() - worldPosition); uint firstItemIndex = clusterData.offset; uint lastItemIndex = firstItemIndex + GET_LIGHT_INDEX(clusterData.counts); [loop] for (uint i = firstItemIndex; i < lastItemIndex; ++i) { uint itemIndex = clusterItemBuffer[i]; uint lightIndex = GET_LIGHT_INDEX(itemIndex); ezPerLightData lightData = perLightDataBuffer[lightIndex]; uint type = (lightData.colorAndType >> 24) & 0xFF; [branch] if (type <= LIGHT_TYPE_DIR) { float3 lightShadowVector; float3 lightDiffuseVector; float3 lightSpecVector; float attenuation = 1.0; float distanceToLight = 1.0; float roughness = 1.0; float specularEnergy = 1.0; float NdotL = EvaluatePBRLight(worldPosition, worldNormal, lightData, type, viewVector, lightShadowVector, lightDiffuseVector, lightSpecVector, attenuation, distanceToLight, roughness, specularEnergy); attenuation *= saturate(lerp(1, NdotL, LightDirectionality)); [branch] if (attenuation > 0.0f) { float3 debugColor = 1.0f; float shadowTerm = 1.0; float subsurfaceShadow = 1.0; [branch] if (lightData.shadowDataOffsetAndFadeOut != 0) { // Zero normal effectively disables normal offset bias. Since our particles may have curved normals the // normal offset bias can create weird artifacts and we don't need this bias on particles as they can't have self-shadow issues. // Our usual noise also doesn't make sense with vertex lighting so we use a fixed shadow filter kernel rotation here. float3 vertexNormal = float3(0, 0, 0); float noise = 0.0; float2x2 fixedRotation = {1, 0, 0, 1}; float extraPenumbraScale = 4.0; shadowTerm = CalculateShadowTerm(worldPosition, vertexNormal, lightShadowVector, distanceToLight, type, lightData.shadowDataOffsetAndFadeOut, noise, fixedRotation, extraPenumbraScale, subsurfaceShadow, debugColor); } attenuation *= lightData.intensity; float3 lightColor = RGB8ToFloat3(lightData.colorAndType); if ((lightData.cookieParams0 & 0xFFFF) != 0) { lightColor *= SampleLightCookie(lightData, worldPosition); } totalLight += lightColor * (attenuation * shadowTerm); } } else // Fill Light { EvaluateFillLight(worldPosition, worldNormal, 1.0, LightDirectionality, lightData, type, totalLight, indirectLightModulation); } } // normalize brdf totalLight *= (1.0f / PI); #endif // sky light in ambient cube basis float3 skyLight = EvaluateAmbientCube(SkyIrradianceTexture, SkyIrradianceIndex, worldNormal).rgb * indirectLightModulation; totalLight += skyLight; return totalLight; }