From 532647c7b91c7e9eaadb7b3a23cfa0320d7de932 Mon Sep 17 00:00:00 2001 From: StarBeats <977663818@qq.com> Date: Fri, 25 Jul 2025 18:05:45 +0800 Subject: [PATCH] sync --- Assets/Scenes/Oasis/OasisScene.unity | 10 +- .../Mobile/Mobile_High_Renderer.asset | 9 +- .../Runtime/Materials/Lit.mat | 9 +- .../AmbientOcclusion/CapsuleAO.cs | 3 + .../AmbientOcclusion/Shader/CapsuleAO.shader | 267 +++++++++++++++++- .../XRenderFeatures/XRenderFeatureManager.cs | 8 +- 6 files changed, 278 insertions(+), 28 deletions(-) diff --git a/Assets/Scenes/Oasis/OasisScene.unity b/Assets/Scenes/Oasis/OasisScene.unity index 28072af..98bcafd 100644 --- a/Assets/Scenes/Oasis/OasisScene.unity +++ b/Assets/Scenes/Oasis/OasisScene.unity @@ -3270,7 +3270,7 @@ MonoBehaviour: m_EditorClassIdentifier: TestLightTrans: {fileID: 634813099} TestCharacterTrans: {fileID: 1880735404} - TestW: 1 + TestW: 5 --- !u!4 &217261640 Transform: m_ObjectHideFlags: 0 @@ -4870,6 +4870,10 @@ PrefabInstance: propertyPath: m_Name value: NestedParentArmature_Unpack objectReference: {fileID: 0} + - target: {fileID: 8508310942573128499, guid: c708a3b79cd542b42bbfedb17e213bc1, type: 3} + propertyPath: m_RenderShadows + value: 0 + objectReference: {fileID: 0} - target: {fileID: 8508310942573128499, guid: c708a3b79cd542b42bbfedb17e213bc1, type: 3} propertyPath: m_RenderPostProcessing value: 1 @@ -20029,8 +20033,8 @@ Transform: m_GameObject: {fileID: 1215335447} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -10.14, y: -3.394, z: -136.12} - m_LocalScale: {x: 0.36361, y: 0.36361, z: 0.36361} + m_LocalPosition: {x: -10.14, y: -3.394, z: -132.664} + m_LocalScale: {x: 0.36361, y: 2.6302094, z: 5.8246684} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} diff --git a/Assets/Settings/Mobile/Mobile_High_Renderer.asset b/Assets/Settings/Mobile/Mobile_High_Renderer.asset index 8727226..6ba74bb 100644 --- a/Assets/Settings/Mobile/Mobile_High_Renderer.asset +++ b/Assets/Settings/Mobile/Mobile_High_Renderer.asset @@ -142,9 +142,10 @@ MonoBehaviour: m_Active: 1 settings: RenderPassEvent: 450 - AmbientIntensity: 0.32 - ShadowIntensity: 0.28 - ShadowSharpness: 0.27 + AmbientIntensity: 1.63 + ShadowIntensity: 0 + ShadowSharpness: 0 + ConeAngle: 8.1 CapsuleAOMat: {fileID: 2100000, guid: c90d40f0d9828744b916dbed0eed9db6, type: 2} --- !u!114 &-5418649131825517062 MonoBehaviour: @@ -610,7 +611,7 @@ MonoBehaviour: MaskMat: {fileID: 2100000, guid: 1368366248809f24e9a110be3075d1b8, type: 2} BlurMat: {fileID: 2100000, guid: 0487cfddca141924d8a1736f6c0b9b7b, type: 2} BlurIterations: 3 - BlurSpread: 0.2 + BlurSpread: 0.5 --- !u!114 &6334271670068977784 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/Materials/Lit.mat b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/Materials/Lit.mat index d16209e..879e6f7 100644 --- a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/Materials/Lit.mat +++ b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/Materials/Lit.mat @@ -15,7 +15,7 @@ MonoBehaviour: version: 7 --- !u!21 &2100000 Material: - serializedVersion: 7 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -24,7 +24,8 @@ Material: m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 - m_ShaderKeywords: + m_ValidKeywords: [] + m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 @@ -144,8 +145,8 @@ Material: - _WorkflowMode: 1 - _ZWrite: 1 m_Colors: - - _BaseColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} - - _Color: {r: 0.5, g: 0.5, b: 0.5, a: 1} + - _BaseColor: {r: 0.7830189, g: 0.7830189, b: 0.7830189, a: 1} + - _Color: {r: 0.7830188, g: 0.7830188, b: 0.7830188, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 1, g: 1, b: 1, a: 1} m_BuildTextureStacks: [] diff --git a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/CapsuleAO.cs b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/CapsuleAO.cs index 57ef4c7..84be89c 100644 --- a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/CapsuleAO.cs +++ b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/CapsuleAO.cs @@ -32,6 +32,8 @@ namespace X.Rendering.Feature public float AmbientIntensity = 0.2f; public float ShadowIntensity = 0.4f; public float ShadowSharpness = 20; + [Range(1, 90)] + public float ConeAngle = 1; public Material CapsuleAOMat; } @@ -119,6 +121,7 @@ namespace X.Rendering.Feature cmd.SetGlobalFloat("_AmbientIntensity", settings.AmbientIntensity); cmd.SetGlobalFloat("_ShadowIntensity", settings.ShadowIntensity); cmd.SetGlobalFloat("_ShadowSharpness", settings.ShadowSharpness); + cmd.SetGlobalFloat("_ConeAngle", settings.ConeAngle); var renderer = renderingData.cameraData.renderer; cmd.SetRenderTarget(renderer.cameraColorTargetHandle, loadAction: RenderBufferLoadAction.Load, storeAction: RenderBufferStoreAction.Store); diff --git a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/Shader/CapsuleAO.shader b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/Shader/CapsuleAO.shader index 2155d39..adb4412 100644 --- a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/Shader/CapsuleAO.shader +++ b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/AmbientOcclusion/Shader/CapsuleAO.shader @@ -48,7 +48,27 @@ Shader "XRP/CapsuleAO" float _ShadowIntensity; float _ShadowSharpness; - + float _ConeAngle; + + #define sq(x) (x * x) + + float acosFast(float x) + { + // Lagarde 2014, "Inverse trigonometric functions GPU optimization for AMD GCN architecture" + // This is the approximation of degree 1, with a max absolute error of 9.0x10^-3 + float y = abs(x); + float p = -0.1565827 * y + 1.570796; + p *= sqrt(1.0 - y); + return x >= 0.0 ? p : PI - p; + } + + float acosFastPositive(float x) + { + // Lagarde 2014, "Inverse trigonometric functions GPU optimization for AMD GCN architecture" + float p = -0.1565827 * x + 1.570796; + return p * sqrt(1.0 - x); + } + Varyings vert(uint vertexID: SV_VertexID) { Varyings o; @@ -78,8 +98,11 @@ Shader "XRP/CapsuleAO" th.x = max(th.x, 0.0001); th.y = saturate(th.y); + //线段ab上离射线最近的点p float3 p = a + ba * th.y; + //射线rord 找到距离线段 ab 最近的点q float3 q = ro + rd * th.x; + //沿着光的方向发射射线, 射线离胶囊体最短的距离 float d = length(p - q) - r; float s = saturate(k * d / th.x + 0.3); @@ -87,10 +110,39 @@ Shader "XRP/CapsuleAO" shadow *= s; } - shadow = saturate(lerp(shadow, 1, intensity)); - return saturate(lerp(shadow, 1, _ShadowIntensity)); + shadow = saturate(lerp(1, shadow, intensity)); + return saturate(lerp(1, shadow, _ShadowIntensity)); } + float _smooth(float x) { return x * x * (3.0 - 2.0 * x); } + + float CalcCapsuleOcclusionFast(float3 p, float3 n, float3 a, float3 b, float r, float maxD) + { + float r2 = r*r; + // Original function but in ^2 space + float3 ba = b - a, pa = p - a; + float h = saturate(dot(pa,ba)/dot(ba,ba)); + float3 d = pa - h*ba; + float l = dot(d,d); + float o = -dot(d,n)*r2*r/(l*l); + // Max dist + o *= _smooth(saturate(1.0 - (l-r2)/(maxD*maxD))); + return saturate(o); + } + + float CalcCapsuleOcclusion(float3 p, float3 n, float3 a, float3 b, float r, float maxD) + { + // closest sphere + float3 ba = b - a, pa = p - a; + float h = saturate(dot(pa,ba)/dot(ba,ba)); + float3 d = pa - h*ba; + float l = length(d); + float o = dot(-d,n)*r*r/(l*l*l); // occlusion of closest sphere + o *= 1.0 + r*(l-r)/(l*l); // multiplier + // o *= 1.0 - saturate((l - r) / maxD); + return saturate(1.0 - o); + } + float CalcCapsuleOcclusionByIndex(float3 p, float3 n, uint s, uint e, float intensity) { float ao = 1.0; @@ -103,15 +155,196 @@ Shader "XRP/CapsuleAO" float3 ba = b - a; float3 pa = p - a; float h = saturate(dot(pa, ba) / dot(ba, ba)); + // 地面点 p 到 ab 最近点 x, 向量 xp = d float3 d = pa - h * ba; float l = length(d); - float o = 1.0 - max(0.0, dot(-d, n)) * r * r / (l * l * l); + float o = 1.0 - max(0.0, dot(-d, n)) * r * r / (l * l * l); + // multiplier o *= 1.0 + r*(l-r)/(l*l); o = sqrt(o * o * o); ao *= o; } + ao = saturate(lerp(1, ao, intensity)); + return clamp(lerp(1, ao, _AmbientIntensity), 0.6, 1); + } + + // Sphere occlusion + float CalcSphereOcclusion( in float3 pos, in float3 nor, in float4 sph ) + { + float3 di = sph.xyz - pos; + float l = length(di); + float nl = (dot(nor,di/l)); + float rad = sph.w; + float v = 1 - rad - nl; + if(l < 0.55) + return abs((1 - rad - nl));// + _ShadowSharpness; - ao = saturate(lerp(ao, 1, intensity)); - return saturate(lerp(ao, 1, _AmbientIntensity)); + + float h = l < rad ? 1.0 : l / rad; + + float h2 = h * h; + float k2 = 1.0 - h2*nl*nl; + + // above/below horizon + // EXACT: Quilez - https://iquilezles.org/articles/sphereao + float res = max(0.0, nl) / h2; + + // intersecting horizon + if( k2 > 0.001 ) + { + #if 0 + // EXACT : Lagarde/de Rousiers - https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf + res = nl * acos(-nl * sqrt((h2 - 1.0) / (1.0 - nl * nl))) - sqrt(k2 * (h2 - 1.0)); + res = res / h2 + atan(sqrt(k2 / (h2 - 1.0))); + res /= 3.141593; + #else + // APPROXIMATED : Quilez - https://iquilezles.org/articles/sphereao + res = (nl * h + 1.0) / h2; + res = 0.33 * res * res; + #endif + } + + return clamp(1.0 - res, 0.6, 1); + } + + float CalcCapsuleOcclusionByIndexV2(float3 p, float3 n, uint s, uint e, float4 cone, float intensity) + { + float ao = 1.0; + for (uint i = s; i < e; ++i) + { + Capsule capsule = _CapsuleData[i + int(_ShadowIntensity)]; + float3 ba = capsule.b - capsule.a; + float3 pa = p - capsule.a; + float l = dot(ba, ba); + // p 在 ba 上投影长度与 ba 长度比值 + float t = /* abs(l) < 1e-8f ? 0.0 : */ saturate(dot(pa, ba) / l); + float3 positionToRay = capsule.a + t * ba; + ao = min(ao, CalcSphereOcclusion(p, n, float4(positionToRay, capsule.radius))); + } + return ao; + ao = saturate(lerp(1, ao, intensity)); + return saturate(lerp(1, ao, _AmbientIntensity)); + } + + // Approximate the area of intersection of two spherical caps, from 'Ambient Aperture Lighting' + // fRadius0 : First caps radius (arc length in radians) + // fRadius1 : Second caps radius (in radians) + // fDist : Distance between caps (radians between centers of caps) + float SphericalCapIntersectionAreaFast(float fRadius0, float fRadius1, float fDist) + { + float fArea; + + if (fDist <= max(fRadius0, fRadius1) - min(fRadius0, fRadius1)) + { + // One cap is completely inside the other + fArea = 6.283185308f - 6.283185308f * cos(min(fRadius0, fRadius1)); + } + else if (fDist >= fRadius0 + fRadius1) + { + // No intersection exists + fArea = 0; + } + else + { + float fDiff = abs(fRadius0 - fRadius1); + fArea = smoothstep(0.0f, + 1.0f, + 1.0f - saturate((fDist - fDiff) / (fRadius0 + fRadius1 - fDiff))); + fArea *= 6.283185308f - 6.283185308f * cos(min(fRadius0, fRadius1)); + } + return fArea; + } + /* + ref: https://developer.amd.com/wordpress/media/2012/10/Oat-AmbientApetureLighting.pdf + Approximate the area of intersection of two spherical caps. + + With some modifcations proposed by the shadertoy implementation + */ + float SphericalCapsIntersectionAreaFast(float cosCap1, float cosCap2, float cap2, float cosDistance) + { + // Precompute constants + float radius1 = acosFastPositive(cosCap1); // First caps radius (arc length in radians) + float radius2 = cap2; // Second caps radius (in radians) + float dist = acosFast(cosDistance); // Distance between caps (radians between centers of caps) + + // Conditional expressions instead of if-else + float check1 = min(radius1, radius2) <= max(radius1, radius2) - dist; + float check2 = radius1 + radius2 <= dist; + + // Ternary operator to replace if-else + float result = check1 ? (1.0 - max(cosCap1, cosCap2)) : (check2 ? 0.0 : 1.0 - max(cosCap1, cosCap2)); + + float delta = abs(radius1 - radius2); + float x = 1.0 - saturate((dist - delta) / max(radius1 + radius2 - delta, FLT_EPS)); + + // simplified smoothstep() + float area = sq(x) * (-2.0 * x + 3.0); + + // Multiply by (1.0 - max(cosCap1, cosCap2)) only once + return area * result; + } + + float DirectionalOcclusionSphere(float3 rayPosition, float3 spherePosition, float sphereRadius, float4 coneProperties) + { + float3 occluderPosition = spherePosition.xyz - rayPosition; + float occluderLength2 = dot(occluderPosition, occluderPosition); + float3 occluderDir = occluderPosition * rsqrt(occluderLength2); + + float cosPhi = dot(occluderDir, coneProperties.xyz); + // sq(sphere.w) should be a uniform --> capsuleRadius^2 + float cosTheta = sqrt(occluderLength2 / (sq(sphereRadius) + occluderLength2)); + float cosCone = cos(coneProperties.w); + + return 1.0 - SphericalCapsIntersectionAreaFast(cosTheta, cosCone, coneProperties.w, cosPhi) / (1.0 - cosCone); + } + + //this sort of work but doesn't support rotations + float DirectionalOcclusionCube(float3 rayPosition, float3 cubePos, float3 cubeSize, float4 cubeRotation, float4 coneProperties) + { + float3 occluder = cubePos.xyz - rayPosition; + occluder /= cubeSize * 0.5f; + + float occluderLength2 = dot(cubeRotation.xyz, occluder); + float3 occluderDir = occluder * rsqrt(occluderLength2); + //occluderDir *= cubeRotation; + //occluderDir = cross(occluderDir, cubeRotation); + + float cosPhi = dot(occluderDir, coneProperties.xyz); + // sq(sphere.w) should be a uniform --> capsuleRadius^2 + float cosTheta = sqrt(occluderLength2 / (sq(1.0f) + occluderLength2)); + float cosCone = cos(coneProperties.w); + + return 1.0 - SphericalCapsIntersectionAreaFast(cosTheta, cosCone, coneProperties.w, cosPhi) / (1.0 - cosCone); + } + + float DirectionalOcclusionCapsule(float3 rayPosition, float3 capsuleA, float3 capsuleB, float capsuleRadius, float4 coneProperties) + { + float3 Ld = capsuleB - capsuleA; + float3 L0 = capsuleA - rayPosition; + float a = dot(coneProperties.xyz, Ld); + float t = saturate(dot(L0, a * coneProperties.xyz - Ld) / (dot(Ld, Ld) - a * a)); + float3 positionToRay = capsuleA + t * Ld; + + return DirectionalOcclusionSphere(rayPosition, positionToRay, capsuleRadius, coneProperties); + } + + float4 GetConeProperties(float3 lightDir) + { + float4 cone = float4(lightDir.xyz, radians(_ConeAngle) * 0.5); + cone.xyz = normalize(cone.xyz); + return cone; + } + + + float CalcCapsuleShadowByIndexV2(float3 ro, float4 cone, uint s, uint e, in float k, float intensity) + { + float shadow = 1.0; + for (uint i = s; i < e; ++i) + { + Capsule c = _CapsuleData[i]; + shadow*= DirectionalOcclusionCapsule(ro, c.a, c.b, c.radius, cone); + } + shadow = saturate(lerp(1, shadow, intensity)); + return saturate(lerp(1, shadow, _ShadowIntensity)); } bool IsInBounds(float3 p, float3 c, float b, out float intensity) @@ -119,7 +352,7 @@ Shader "XRP/CapsuleAO" float3 diff = abs(p - c); if (diff.x < b && diff.y < b && diff.z < b) { - intensity = saturate(dot(diff, diff) / dot(b, b)); + intensity = saturate(dot(b, b) / dot(diff.xz, diff.xz)); return true; } intensity = 0; @@ -130,20 +363,25 @@ Shader "XRP/CapsuleAO" { float shadow = 1.0; float occlusion = 1.0; + float4 cone = GetConeProperties(_MainLightPosition.xyz); + for (uint i = 0; i < _CharactersCount; ++i) { - float intensity; + float intensity = 1; Character c = _CharacterData[i]; - if (IsInBounds(worldPos, c.position, c.radius, intensity)) + // if (IsInBounds(worldPos, c.position, c.radius*10, intensity)) { - float tempIntensity = intensity / saturate(1 * smoothstep(0.001, 1, c.lightDir.w)); - float tempShadow = CalcCapsuleShadowByIndex(worldPos, _MainLightPosition.xyz, c.startID, c.endID, _ShadowSharpness, tempIntensity); - shadow = min(shadow, tempShadow); - occlusion *= CalcCapsuleOcclusionByIndex(worldPos, worldNormal, c.startID, c.endID, intensity); + float tempIntensity = intensity / saturate(1 * smoothstep(0.1, 2, c.lightDir.w)); + // float tempShadow = CalcCapsuleShadowByIndex(worldPos, _MainLightPosition.xyz, c.startID, c.endID, _ShadowSharpness, tempIntensity); + // float tempShadow = CalcCapsuleShadowByIndexV2(worldPos, cone, c.startID, c.endID, _ShadowSharpness, tempIntensity); + // shadow = min(shadow, tempShadow); + // occlusion *= CalcCapsuleOcclusionByIndex(worldPos, worldNormal, c.startID, c.endID, intensity); + occlusion *= CalcCapsuleOcclusionByIndexV2(worldPos, worldNormal, c.startID, c.endID,cone, intensity); } } return shadow * occlusion; } + float4 frag(Varyings input) : SV_Target { @@ -160,6 +398,7 @@ Shader "XRP/CapsuleAO" float4 worldPos = mul(UNITY_MATRIX_I_VP, float4(sceneUV, depth, 1)); worldPos /= worldPos.w; float3 worldNormal = SampleSceneNormals(uv); + float ao = CalcAmbientOcclusion(worldPos.xyz, worldNormal); // float4 shadowCoord = TransformWorldToShadowCoord(worldPos.xyz); @@ -167,7 +406,7 @@ Shader "XRP/CapsuleAO" // //只在阴影处产生AO // ao = lerp(ao, 1, step(0.5, mainLight.shadowAttenuation)); - return half4(1, 0, 0, ao); + return half4(ao, 0, 0, ao); } ENDHLSL diff --git a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/XRenderFeatureManager.cs b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/XRenderFeatureManager.cs index dbf190d..8dce49c 100644 --- a/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/XRenderFeatureManager.cs +++ b/Packages/com.unity.render-pipelines.universal@14.0.11/Runtime/XRenderFeatures/XRenderFeatureManager.cs @@ -83,8 +83,10 @@ namespace X.Rendering.Feature { var capsule = item.Value[i]; //capsule.direction - var a = capsule.center - new Vector3(0, capsule.height / 2, 0); - var b = capsule.center + new Vector3(0, capsule.height / 2, 0); + var a = capsule.center - new Vector3(0, capsule.height / 2 - capsule.radius, 0); + var b = capsule.center + new Vector3(0, capsule.height / 2 - capsule.radius, 0); + //var a = capsule.center - new Vector3(0, capsule.height / 2, 0); + //var b = capsule.center + new Vector3(0, capsule.height / 2, 0); a = capsule.transform.TransformPoint(a); b = capsule.transform.TransformPoint(b); CapsuleArray[capsuleArrayIndex++] = new() @@ -101,7 +103,7 @@ namespace X.Rendering.Feature CharacterArray[characterArrayIndex++] = new() { position = transform.position, - radius = 5, + radius = transform.lossyScale.x, lightDir = -new Vector4(TestLightTrans.position.x, TestLightTrans.position.y, TestLightTrans.position.z, -TestW), startID = startID, endID = capsuleArrayIndex,