• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
1
Question by JJJohan1 · Sep 10, 2015 at 01:30 AM · shaderlightingparticles

Need some assistance with spot/point lighting for GPU based particles

Hello, I've been trying fruitlessly for some time now to get lighting working with my GPU powered particle system. Due to the use of a geometry shader I can't rely on simply using a surface shader to do my lighting for me, so I'm trying to use some older shader examples.

I've used the CG Programming reference from here to get lighting working to an extent, however both point light range and spotlights are not taking effect correctly. I don't have the need for a ForwardAdd pass as 4 lights is all I'll need at most. https://en.wikibooks.org/wiki/Cg_Programming/Unity/Multiple_Lights

I have also had some partial success with a surface shader still using SV_InstanceID/SV_VertexID from this Reddit thread: https://www.reddit.com/r/Unity3D/comments/33psb4/graphicsdrawprocedural_in_normal_render_queue/

However I wasn't able to get point or spotlights to work and was also limited with not being able to calculate normals correctly as I'd only be able to apply normals to a single set of 6 vertices that are instanced.

Here's my shader code, modified for my purpose using the base code from the first link:

 Shader "Custom/WaterParticle" 
 {
     // Bound with the inspector.
     Properties 
     {
         _MainTex("Base (RGB)", 2D) = "white" {}
         _Color ("Main Color", Color) = (1, 1, 1, 1)
         _Size ("Size", float) = 1.0
     }
 
     SubShader 
     {
         Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
 
         Pass 
         {    
             Tags { "LightMode" = "ForwardBase" } 
             Blend SrcAlpha OneMinusSrcAlpha
             ZWrite Off
  
             CGPROGRAM
             #pragma target 5.0
 
             #pragma vertex vert  
             #pragma geometry GS_Main
             #pragma fragment frag 
             #pragma multi_compile_fwdbase
  
             #include "UnityCG.cginc" 
             #include "AutoLight.cginc"
 
             // User-specified properties
             uniform float4 _Color;
             uniform float4 _LightColor0;
             uniform sampler2D _MainTex;
             uniform float _Size;
 
             // The same particle data structure used by both the compute shader and the shader.
             struct Particle
             {
                 float3 Position;
                 float3 Normal;
                 float3 StartPosition;
                 float3 Velocity;
                 float LifeTime;
                 float Life;
                 int Sprite;
             };
 
             struct GS_INPUT
             {
                 float4 pos : SV_POSITION;
                 float4 col : COLOR;
                 float3 normal : TEXCOORD1;
                 float2 uv : TEXCOORD2;
                 float3 lightColour : TEXCOORD3;
             };
 
             struct FS_INPUT
             {
                 float4 pos : SV_POSITION;
                 float4 worldPos : TEXCOORD0;
                 float3 normal : TEXCOORD1;
                 float2 uv : TEXCOORD2;
                 float3 lightColour : TEXCOORD3;
                 float4 col : TEXCOORD4;
             };
             
             // The buffer holding the particles shared with the compute shader.
             StructuredBuffer<Particle> particleBuffer;
  
             // DX11 vertex shader these 2 parameters come from the draw call: "1" and "particleCount", 
             // SV_VertexID: "1" is the number of vertex to draw peer particle, we could easily make quad or sphere particles with this.
             // SV_InstanceID: "particleCount", number of particles...
             GS_INPUT vert (uint id : SV_VertexID, uint inst : SV_InstanceID)
             {
                 GS_INPUT output;
              
                 float3 up = float3(0, 1, 0);
                 output.normal = normalize(_WorldSpaceCameraPos - mul(_Object2World, float4(particleBuffer[inst].Position, 1)));
 
                 // color computation
                 output.col = _Color;
 
                 // Fade out
                 if (particleBuffer[inst].Life < 1.0f)
                 {
                     output.col.a *= particleBuffer[inst].Life;
                 }
                 else if (particleBuffer[inst].Life > 4.0f)
                 {
                     output.col.a *= 1.0f - (particleBuffer[inst].Life - 4.0f);
                 }
 
                 // position computation
                 output.pos = mul(_Object2World, float4(particleBuffer[inst].Position, 1));
 
                 // Use W component for sprite index
                 output.pos.w = particleBuffer[inst].Sprite;
 
                 output.uv = float2(0, 0);
 
                 // Diffuse reflection by four "vertex lights"            
                 output.lightColour = float3(0.0, 0.0, 0.0);
                 #ifdef VERTEXLIGHT_ON
                     for (int index = 0; index < 4; index++)
                     {    
                        float4 lightPosition = float4(unity_4LightPosX0[index], 
                           unity_4LightPosY0[index], 
                           unity_4LightPosZ0[index], 1.0);
  
                        float3 vertexToLightSource = lightPosition.xyz - output.posWorld.xyz;        
                        float3 lightDirection = normalize(vertexToLightSource);
                        float squaredDistance = dot(vertexToLightSource, vertexToLightSource);
                        float attenuation = 1.0 / (1.0 + unity_4LightAtten0[index] * squaredDistance);
                        float3 diffuseReflection = attenuation * unity_LightColor[index].rgb * _Color.rgb 
                           * max(0.0, dot(output.normal, lightDirection));         
  
                        output.lightColour = output.lightColour + diffuseReflection;
                     }
                 #endif
 
                 return output;
             }
 
              // Geometry Shader -----------------------------------------------------
             [maxvertexcount(8)]
             void GS_Main(point GS_INPUT p[1], inout TriangleStream<FS_INPUT> triStream)
             {
                 float3 up = float3(0, 1, 0);
                 float3 look = _WorldSpaceCameraPos - p[0].pos;
                 look = normalize(look);
                 float3 right = cross(up, look);
                     
                 float halfS = 0.5f * _Size;
                             
                 float4 v[4];
                 v[0] = float4(p[0].pos + halfS * right - halfS * up, 1.0f);
                 v[1] = float4(p[0].pos + halfS * right + halfS * up, 1.0f);
                 v[2] = float4(p[0].pos - halfS * right - halfS * up, 1.0f);
                 v[3] = float4(p[0].pos - halfS * right + halfS * up, 1.0f);
 
                 float4x4 vp = mul(UNITY_MATRIX_MVP, _World2Object);
                 FS_INPUT pIn;
                 pIn.normal = p[0].normal;
                 pIn.lightColour = p[0].lightColour;
                 pIn.col = p[0].col;
 
                 pIn.pos = mul(vp, v[0]);
                 pIn.worldPos = v[0];
                 pIn.uv = float2(1.0f, 0.0f + (0.1f * p[0].pos.w));
                 triStream.Append(pIn);
 
                 pIn.pos =  mul(vp, v[1]);
                 pIn.worldPos = v[1];
                 pIn.uv = float2(1.0f, 0.1f + (0.1f * p[0].pos.w));
                 triStream.Append(pIn);
 
                 pIn.pos =  mul(vp, v[2]);
                 pIn.worldPos = v[2];
                 pIn.uv = float2(0.0f, 0.0f + (0.1f * p[0].pos.w));
                 triStream.Append(pIn);
 
                 pIn.pos =  mul(vp, v[3]);
                 pIn.worldPos = v[3];
                 pIn.uv = float2(0.0f, 0.1f + (0.1f * p[0].pos.w));
                 triStream.Append(pIn);
             }
  
             float4 frag (FS_INPUT input) : COLOR
             {
                 float3 normalDirection = input.normal; 
                 float3 lightDirection;
                 float attenuation;
  
                 if (0.0 == _WorldSpaceLightPos0.w) // directional light?
                 {
                    attenuation = 1.0; // no attenuation
                    lightDirection = normalize(_WorldSpaceLightPos0.xyz);
                 } 
                 else // point or spot light
                 {
                    float3 vertexToLightSource = 
                       _WorldSpaceLightPos0.xyz - input.worldPos.xyz;
                    float distance = length(vertexToLightSource);
                    attenuation = 1.0 / distance; // linear attenuation 
                    lightDirection = normalize(vertexToLightSource);
                 }
  
                 float3 ambientLighting = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
                 float3 diffuseReflection = attenuation * _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection));
                 float3 specularReflection;
 
                 if (dot(normalDirection, lightDirection) < 0.0) // light source on the wrong side?
                 {
                    specularReflection = float3(0.0, 0.0, 0.0); // no specular reflection
                 }
                 else // light source on the right side
                 {
                    specularReflection = attenuation * _LightColor0.rgb 
                       * pow(max(0.0, dot(
                       reflect(-lightDirection, normalDirection), 
                       normalDirection)), 10);
                 }
  
                 // Lighting output
                 float4 lightOutput = float4(input.lightColour + ambientLighting 
                    + diffuseReflection + specularReflection, 1.0);
 
                 return tex2D(_MainTex, input.uv) * input.col * lightOutput;
             }
             ENDCG
         }
     }
 
     Fallback "Specular"
 }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

28 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

How to make particles react to scene lighting 3 Answers

Unity 2D sprite Renderer + Material Sprite/Diffuse shader not working 1 Answer

Just Cause 3 Volumetric Explosions 1 Answer

Light Flickering on every object except the terrain. 1 Answer

Textures are pure white in project window 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges