From 932209b16aad9b0f77eb2f84449b7addebf0aa48 Mon Sep 17 00:00:00 2001 From: lghassen Date: Tue, 25 Aug 2020 22:14:39 +0200 Subject: [PATCH] disable particle to texture rendering on OpenGL (still need to fix particles fading near far plane of near camera) --- .../GeometryCloudVolumeParticle.shader | 333 ++++++++++++++++++ ...ometryCloudVolumeParticleToTexture.shader} | 0 Atmosphere/CloudsVolume.cs | 28 +- Atmosphere/VolumeSection.cs | 19 +- Utils/Tools.cs | 5 + 5 files changed, 376 insertions(+), 9 deletions(-) create mode 100644 Assets/Shaders/GeometryCloudVolumeParticle.shader rename Assets/Shaders/{CloudVolumeParticle.shader => GeometryCloudVolumeParticleToTexture.shader} (100%) diff --git a/Assets/Shaders/GeometryCloudVolumeParticle.shader b/Assets/Shaders/GeometryCloudVolumeParticle.shader new file mode 100644 index 00000000..6331a448 --- /dev/null +++ b/Assets/Shaders/GeometryCloudVolumeParticle.shader @@ -0,0 +1,333 @@ +// Instead of having one draw call per particle quad like in original EVE +// Here we have a single draw call passing a points mesh to the GPU +// This shader then builds all the quads from the mesh using a geometry shader +// This saves a ton of draw calls +Shader "EVE/GeometryCloudVolumeParticle" { + Properties { + _Tex("Particle Texture", 2D) = "white" {} + _MainTex("Main (RGB)", 2D) = "white" {} + _PerlinTex("Perlin (RGB)", 2D) = "white" {} + _BumpMap("Normalmap", 2D) = "bump" {} + _DetailTex("Detail (RGB)", 2D) = "white" {} + _DetailScale("Detail Scale", Range(0,1000)) = 100 + _DistFade("Distance Fade Near", Float) = 1.0 + _DistFadeVert("Distance Fade Vertical", Float) = 0.0004 + _MinScatter("Min Scatter", Float) = 1.05 + _Opacity("Opacity", Float) = 1.05 + _Color("Color Tint", Color) = (1,1,1,1) + _InvFade("Soft Particles Factor", Range(0,1.0)) = .008 + _Rotation("Rotation", Float) = 0 + _MaxScale("Max Scale", Float) = 1 + _MaxTrans("Max Translation", Vector) = (0,0,0) + _NoiseScale("Noise Scale", Vector) = (1,2,.0005,100) + _SunPos("_SunPos", Vector) = (0,0,0) + _SunRadius("_SunRadius", Float) = 1 + } + + Category { + + Tags { "Queue"="Transparent-1" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" } + Blend SrcAlpha OneMinusSrcAlpha + Fog { Mode Global} + AlphaTest Greater 0 + ColorMask RGB + //Cull Back + Cull Off + Lighting On + ZWrite Off + + SubShader { + Pass { + + Lighting On + Tags { "LightMode"="ForwardBase"} + + CGPROGRAM + #include "EVEUtils.cginc" + #pragma vertex vert + #pragma fragment frag + #pragma geometry geom + #define MAG_ONE 1.4142135623730950488016887242097 + #pragma fragmentoption ARB_precision_hint_fastest + #pragma multi_compile_fwdbase + #pragma multi_compile SOFT_DEPTH_OFF SOFT_DEPTH_ON +#pragma multi_compile MAP_TYPE_1 MAP_TYPE_CUBE_1 MAP_TYPE_CUBE2_1 MAP_TYPE_CUBE6_1 +#ifndef MAP_TYPE_CUBE2_1 +#pragma multi_compile ALPHAMAP_N_1 ALPHAMAP_1 +#endif + + #include "noiseSimplex.cginc" + #include "alphaMap.cginc" + #include "cubeMap.cginc" + + CUBEMAP_DEF_1(_MainTex) + + sampler2D _Tex; + sampler2D _DetailTex; + sampler2D _BumpMap; + + float4x4 _PosRotation; + + float _DetailScale; + fixed4 _Color; + float _DistFade; + float _DistFadeVert; + float _MinScatter; + float _Opacity; + float _InvFade; + float _Rotation; + float _QuadSize; + float _MaxScale; + float4 _NoiseScale; + float3 _MaxTrans; + + sampler2D _CameraDepthTexture; + + // float4x4 _CameraToWorld; + + struct appdata_t { + float4 vertex : POSITION; + fixed4 color : COLOR; + float3 normal : NORMAL; + float4 tangent : TANGENT; + float2 texcoord : TEXCOORD0; + }; + + struct v2g { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + float3 hashVect : TEXCOORD0; + float4 localOrigin : TEXCOORD1; + float4 origin : TEXCOORD2; + float viewDirFade : TEXCOORD3; + float3 planetPos : TEXCOORD4; + }; + + //vertex function, called for every point on our hexSeg, each vertex corresponding to the origin of a particle/quad + v2g vert (appdata_t v) + { + v2g o; + UNITY_INITIALIZE_OUTPUT(v2g, o); + + v.vertex.xyz/=v.vertex.w; + float4 origin = mul(unity_ObjectToWorld, v.vertex); //origin of the quad in worldSpace + + float4 planet_pos = mul(_PosRotation, origin); + //these two operations we can make directly in one op by doing mul(_PosRotation, Object2World) on CPU + //still the vertex shader cost of this shader is negligible in front of the fragment shader so these things aren't really necessary + + float3 normalized = _NoiseScale.z*(planet_pos.xyz); + float3 hashVect = .5*(float3(snoise(normalized), snoise(_NoiseScale.x*normalized), snoise(_NoiseScale.y*normalized))+1); //unique hash of the particle based on planet pos + + o.hashVect = hashVect; + + float4 localOrigin; + localOrigin.xyz = (2*hashVect-1)*_MaxTrans; //offset to localOrigin based on hash above + localOrigin.w = 1; + + //localOrigin.xyz+=v.vertex.xyz; //here this is wrong, in the original shader, local origin is added to the quad in it's space and gets transformed with it'w own M matrix with no issues + //here as we add this to the space of the hex, transforming later with M matrix can cause additional rotation, we can add this in worldSpace instead + + origin.xyz+=localOrigin; //the particle transforms are originally oriented same as in world space, therefore we can do this offset directly in worldSpace in our case + o.origin = origin; + + localOrigin = mul (unity_WorldToObject, origin); //transform back to find the new localOrigin + o.localOrigin = localOrigin; + + planet_pos = mul(_MainRotation, origin); //new planet pos based on offset origin + o.planetPos = planet_pos.xyz; + + float3 detail_pos = mul(_DetailRotation, planet_pos).xyz; + o.color = VERT_GET_NO_LOD_CUBE_MAP_1(_MainTex, planet_pos.xyz); + o.color.rgba *= GetCubeDetailMapNoLOD(_DetailTex, detail_pos, _DetailScale); + + o.viewDirFade = GetDistanceFade(distance(origin, _WorldSpaceCameraPos), _DistFade, _DistFadeVert); + o.color.a *= o.viewDirFade; + + float4 mvCenter = mul(UNITY_MATRIX_MV, float4(localOrigin.xyz,1.0)); //offset quad origin in viewspace + o.pos=mvCenter; + o.pos = o.color.a > (1.0/255.0) ? o.pos : float4(2.0, 2.0, 2.0, 1.0); //cull vertex if low alpha, pos outside clipspace + + return o; + } + + + struct g2f + { + float4 pos : SV_POSITION; + fixed4 color : COLOR; + half4 viewDir : TEXCOORD0; + float2 texcoordZY : TEXCOORD1; + float2 texcoordXZ : TEXCOORD2; + float2 texcoordXY : TEXCOORD3; + float2 uv : TEXCOORD4; + float4 projPos : TEXCOORD5; + float3 planetPos : TEXCOORD6; + float3 viewDirT : TEXCOORD7; + float3 lightDirT : TEXCOORD8; + float4 localOrigin : TEXCOORD9; + }; + + //this function builds a quad corner vertex from the data passed to it, it will be called by the geometry shader + g2f buildQuadVertex(v2g originPoint, float3 vertexPosition, float2 vertexUV, float3 viewDir, float4x4 mvMatrix, float localScale) + { + g2f tri; + UNITY_INITIALIZE_OUTPUT(g2f, tri); + + float3 mvCenter = originPoint.pos.xyz/originPoint.pos.w; + tri.pos = float4(mvCenter + _QuadSize * localScale * vertexPosition,1.0); //position in view space of quad corner + +#ifdef SOFT_DEPTH_ON + float eyedepth = -tri.pos.z; +#endif + + tri.pos = mul (UNITY_MATRIX_P, tri.pos); + +#ifdef SOFT_DEPTH_ON + tri.projPos = ComputeScreenPos (tri.pos); + //COMPUTE_EYEDEPTH(tri.projPos.z); + + //we replace COMPUTE_EYEDEPTH with it's definition as it's only designed for the vertex shader input + //tri.projPos.z = -UnityObjectToViewPos( v.vertex ).z + tri.projPos.z = eyedepth; +#endif + + + + //pass these values we need for the fragment shader + tri.viewDir.xyz = abs(viewDir).xyz; + tri.viewDir.w = originPoint.viewDirFade; + tri.planetPos = originPoint.planetPos; + tri.color = originPoint.color; + + float2 texcoodOffsetxy = ((2*vertexUV)- 1); + float4 texcoordOffset = float4(texcoodOffsetxy.x, texcoodOffsetxy.y, 0, 1.0); //would 1.0 work here???? let's find out + + float4 ZYv = texcoordOffset.zyxw; + float4 XZv = texcoordOffset.xzyw; + float4 XYv = texcoordOffset.xyzw; + + ZYv.z*=sign(-viewDir.x); + XZv.x*=sign(-viewDir.y); + XYv.x*=sign(viewDir.z); + + ZYv.x += sign(-viewDir.x)*sign(ZYv.z)*(viewDir.z); + XZv.y += sign(-viewDir.y)*sign(XZv.x)*(viewDir.x); + XYv.z += sign(-viewDir.z)*sign(XYv.x)*(viewDir.x); + + ZYv.x += sign(-viewDir.x)*sign(ZYv.y)*(viewDir.y); + XZv.y += sign(-viewDir.y)*sign(XZv.z)*(viewDir.z); + XYv.z += sign(-viewDir.z)*sign(XYv.y)*(viewDir.y); + + float2 ZY = mul(mvMatrix, ZYv).xy - mvCenter.xy; + float2 XZ = mul(mvMatrix, XZv).xy - mvCenter.xy; + float2 XY = mul(mvMatrix, XYv).xy - mvCenter.xy; + + tri.texcoordZY = half2(.5 ,.5) + .6*(ZY); + tri.texcoordXZ = half2(.5 ,.5) + .6*(XZ); + tri.texcoordXY = half2(.5 ,.5) + .6*(XY); + + viewDir = normalize(originPoint.origin - _WorldSpaceCameraPos); //worldSpaceView dir to center of quad not to fragment, why? maybe to frag would be better? + + half3 normal = normalize(-viewDir); + float3 tangent = UNITY_MATRIX_V[0].xyz; + float3 binormal = -cross(normal, normalize(tangent)); + float3x3 rotation = float3x3(tangent.xyz, binormal, normal); + + tri.lightDirT = normalize(mul(rotation, _WorldSpaceLightPos0.xyz)); + tri.viewDirT = normalize(mul(rotation, viewDir)); + + tri.uv = vertexUV; //quad UV + tri.localOrigin = originPoint.localOrigin; + + return tri; + } + + //geometry shader + //for every vertex from the vertex shader it will create a particle quad of 4 vertexes and 2 triangles + [maxvertexcount(4)] + void geom(point v2g input[1], inout TriangleStream outStream) + { + g2f tri; + + + //common values for all the quad + float localScale = (input[0].hashVect.x*(_MaxScale - 1)) + 1; + + float4x4 M = rand_rotation( + (float3(frac(_Rotation),0,0))+input[0].hashVect, + localScale, + input[0].localOrigin.xyz/input[0].localOrigin.w); + + float4x4 mvMatrix = mul(mul(UNITY_MATRIX_V, unity_ObjectToWorld), M); + float3 viewDir = normalize(mvMatrix[2].xyz); //cameraSpace viewDir I think + + //build our quad + tri = buildQuadVertex(input[0],float3(-0.5,-0.5,0.0),float2(0.0,0.0),viewDir,mvMatrix,localScale); + outStream.Append(tri); + + tri = buildQuadVertex(input[0],float3(-0.5,0.5,0.0),float2(0.0,1.0),viewDir,mvMatrix,localScale); + outStream.Append(tri); + + tri = buildQuadVertex(input[0],float3(0.5,-0.5,0.0),float2(1.0,0.0),viewDir,mvMatrix,localScale); + outStream.Append(tri); + + tri = buildQuadVertex(input[0],float3(0.5,0.5,0.0),float2(1.0,1.0),viewDir,mvMatrix,localScale); + outStream.Append(tri); + + outStream.RestartStrip(); + } + + float4 frag (g2f IN) : COLOR + { + + half4 tex; + tex.r = tex2D(_Tex, IN.texcoordZY).r; + tex.g = tex2D(_Tex, IN.texcoordXZ).g; + tex.b = tex2D(_Tex, IN.texcoordXY).b; + + tex.a = 0; + + tex.rgb *= IN.viewDir.rgb; + half4 vect = half4( IN.viewDir.rgb, 0); + tex /= vectorSum(vect); + + tex = half4(1, 1, 1, vectorSum(tex)); + + half4 color = FRAG_GET_NO_LOD_CUBE_MAP_1(_MainTex, IN.planetPos); + color = ALPHA_COLOR_1(color); + + color *= _Color * IN.color; + + color.a *= tex.a; + tex.a = IN.viewDir.w*tex.a; + + + //half3 normT = UnpackNormal(tex2D(_BumpMap, IN.uv)); + half3 normT; + normT.xy = ((2*IN.uv)-1); + normT.z = sqrt(1 - saturate(dot(normT.xy, normT.xy))); + //normT.xy = 2 * INV_PI*asin((2 * IN.uv) - 1) ; + //normT.xy = sin(PI*(IN.uv-.5)); + //normT.z = 1; + //color.rg = IN.uv; + + color.rgb *= ScatterColorLight(IN.lightDirT, IN.viewDirT, normT, tex, _MinScatter, _Opacity, 1).rgb; + +#ifdef SOFT_DEPTH_ON + float depth = UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(IN.projPos))); + depth = LinearEyeDepth (depth); + float partZ = IN.projPos.z; + float fade = saturate (_InvFade * (depth-partZ)); + color.a *= fade; +#endif + + return color; + } + ENDCG + } + + } + + } +} \ No newline at end of file diff --git a/Assets/Shaders/CloudVolumeParticle.shader b/Assets/Shaders/GeometryCloudVolumeParticleToTexture.shader similarity index 100% rename from Assets/Shaders/CloudVolumeParticle.shader rename to Assets/Shaders/GeometryCloudVolumeParticleToTexture.shader diff --git a/Atmosphere/CloudsVolume.cs b/Atmosphere/CloudsVolume.cs index 902624ec..f9a126ec 100644 --- a/Atmosphere/CloudsVolume.cs +++ b/Atmosphere/CloudsVolume.cs @@ -66,8 +66,21 @@ private static Shader ParticleCloudShader { if (particleCloudShader == null) { - particleCloudShader = ShaderLoaderClass.FindShader("EVE/GeometryCloudVolumeParticleToTexture"); - } return particleCloudShader; + particleCloudShader = ShaderLoaderClass.FindShader("EVE/GeometryCloudVolumeParticle"); + } + return particleCloudShader; + } + } + + private static Shader particleCloudToTextureShader = null; + private static Shader ParticleCloudToTextureShader + { + get + { + if (particleCloudToTextureShader == null) + { + particleCloudToTextureShader = ShaderLoaderClass.FindShader("EVE/GeometryCloudVolumeParticleToTexture"); + } return particleCloudToTextureShader; } } @@ -90,7 +103,16 @@ public void Apply(CloudsMaterial material, float radius, Transform parent) particleMaterial.MaxScale = size.y; particleMaterial.MaxTrans = maxTranslation; particleMaterial.NoiseScale = new Vector3(noiseScale.x, noiseScale.y, noiseScale.z / radius); - ParticleMaterial = new Material(ParticleCloudShader); + + if (Tools.IsUnifiedCameraMode()) + { + ParticleMaterial = new Material(ParticleCloudToTextureShader); + } + else + { + ParticleMaterial = new Material(ParticleCloudShader); + } + particleMaterial.ApplyMaterialProperties(ParticleMaterial); material.ApplyMaterialProperties(ParticleMaterial); ParticleMaterial.EnableKeyword("SOFT_DEPTH_ON"); diff --git a/Atmosphere/VolumeSection.cs b/Atmosphere/VolumeSection.cs index b1a5df3a..fe638597 100644 --- a/Atmosphere/VolumeSection.cs +++ b/Atmosphere/VolumeSection.cs @@ -86,16 +86,23 @@ public CloudMesh(Material cloudParticleMaterial, Vector2 size, Transform parent, //Debug.Log("vertices.Count() " + vertices.Count()); - MeshRenderer mr = cloudMesh.AddComponent(); - //mr.sharedMaterial = cloudParticleMaterial; - mr.sharedMaterial = new Material(InvisibleShader); //this may throw off scatterer's integration though, so check that + + MeshRenderer mr = cloudMesh.AddComponent(); + + if (Tools.IsUnifiedCameraMode()) + { + mr.sharedMaterial = new Material(InvisibleShader); + DeferredRendererNotifier notifier = cloudMesh.AddComponent(); + notifier.mat = cloudParticleMaterial; + } + else + { + mr.sharedMaterial = cloudParticleMaterial; + } mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false; mr.enabled = true; - - DeferredRendererNotifier notifier = cloudMesh.AddComponent(); - notifier.mat = cloudParticleMaterial; } private static Shader invisibleShader = null; diff --git a/Utils/Tools.cs b/Utils/Tools.cs index 9ba4c5d3..62b9191b 100644 --- a/Utils/Tools.cs +++ b/Utils/Tools.cs @@ -149,5 +149,10 @@ public static GameObject GetMainMenuObject(string name) } return fallback; } + + public static bool IsUnifiedCameraMode() + { + return SystemInfo.graphicsDeviceVersion.Contains("Direct3D 11.0"); + } } }