Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

World coordinates for texture on procedural mesh

Discussion in 'Shaders' started by stulleman, Sep 30, 2015.

  1. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Hello!

    I asked this question on Unity Answers but didn't get any response.
    Anyway this is what I want to do:

    I have the basic lighting-mesh generation ready:



    Now I wanted to apply a circular texture to this mesh so it looks like a real light. The mesh is always centered around the lamp.

    I thought in using the world coordinates somehow, but I don't get anything to work. Do you have any ideas?

    Thanks!
     
  2. MSplitz-PsychoK

    MSplitz-PsychoK

    Joined:
    May 16, 2015
    Posts:
    1,278
    I think you might find you actually want screen-space UVs instead of world space, or maybe offset the UVs by the players position so it centers there.

    Aaanyway, your UVs should be equal to "mul(_Object2World, vertex).xy". This will put your vertex in world-space and will not skew it with the camera's perspective. This will be a little more complicated if you have to rotate your camera, and I'm also assuming your camera.forward is looking down the Z axis.

    Here's a page that lists Unity's built-in shader matrices: http://docs.unity3d.com/462/Documentation/Manual/SL-BuiltinValues.html
     
  3. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Thank you for your answer.

    Since I have very limited shader knowledge, I still don't know how to do it.

    Just to show you that I'm trying :D
    EDIT: I changed the code many times because nothing worked. So this might be even more useless then I what I had in the beginning :D

    Code (CSharp):
    1. Shader "Custom/PointLight" {
    2.  
    3.     Properties {
    4.         _Color ("Color", Color) = (1,1,1,1)
    5.         _LightPosition ("Light position", Vector) = (0,0,0,0)
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.     }
    8.  
    9.     SubShader {
    10.      
    11.         Pass {
    12.  
    13.             CGPROGRAM
    14.  
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #include "UnityCG.cginc"
    18.  
    19.             struct VertexInput {
    20.                 float3 Position : POSITION;
    21.                 float2 TexCoord : TEXCOORD0;
    22.             };
    23.  
    24.             struct VertexOutput {
    25.                 float4 Position : POSITION0;
    26.                 float2 WorldPos : TEXCOORD0;
    27.             };
    28.  
    29.             VertexOutput vert(VertexInput i) {
    30.  
    31.                 VertexOutput output;
    32.                 output.Position = mul (UNITY_MATRIX_MVP, i.Position);
    33.                 output.WorldPos = i.TexCoord;
    34.                 return output;
    35.  
    36.             }
    37.      
    38.             uniform float4 _Color;
    39.             uniform sampler2D _MainTex;
    40.             uniform float2 _LightPosition;
    41.  
    42.             float4 frag(VertexOutput i) : SV_Target {
    43.  
    44.                 float2 position = i.WorldPos - _LightPosition;
    45.                 float dist = sqrt(dot(position, position));
    46.  
    47.                 float3 mix = lerp(_Color, float4(0,0,0,0), saturate(dist));
    48.                 return float4(mix, 1.0f);
    49.  
    50.             }
    51.  
    52.             ENDCG
    53.  
    54.         }
    55.     }
    56. }
    It probably doesn't make any sense.
    Can you give me a hint where I have to do what?

    Thanks!
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    output.WorldPosition = mul(_Object2World, input.Position).xy;
     
  5. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Okay so now I have this:

    Code (CSharp):
    1. Shader "Custom/PointLight" {
    2.  
    3.     Properties {
    4.         //_Color ("Color", Color) = (1,1,1,1)
    5.         //_LightPosition ("Light position", Vector) = (0,0,0,1)
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.     }
    8.  
    9.     SubShader {
    10.        
    11.         Pass {
    12.  
    13.             CGPROGRAM
    14.  
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #include "UnityCG.cginc"
    18.  
    19.             struct VertexInput {
    20.                 float3 Position : POSITION;
    21.                 float2 TexCoord : TEXCOORD0;
    22.             };
    23.  
    24.             struct VertexOutput {
    25.                 float4 Position : POSITION0;
    26.                 float2 WorldPos : TEXCOORD0;
    27.             };
    28.  
    29.             VertexOutput vert(VertexInput i) {
    30.  
    31.                 VertexOutput output;
    32.                 output.Position = mul (UNITY_MATRIX_MVP, i.Position);
    33.                 output.WorldPos = mul(_Object2World, i.Position).xy;
    34.                 return output;
    35.  
    36.             }
    37.  
    38.             uniform sampler2D _MainTex;
    39.  
    40.             float4 frag(VertexOutput i) : SV_Target {
    41.  
    42.                 return tex2D(_MainTex, i.WorldPos);
    43.  
    44.             }
    45.  
    46.             ENDCG
    47.  
    48.         }
    49.     }
    50. }
    Since I'm not using the uv coordinates, I assume I don't have to do anything with them when I build the mesh?
    The problem with this code is that the mesh is screwed up and seems to extend infinitely in x and y.
    So with this shader the screen is just black.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    On vertex input the position should be a float4. On output it should be to SV_POSITION, not POSITION0.
     
  7. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Sorry for the late answer.
    And thanks, now it works.



    The problem now is that my shader doesn't support alpha transparency. So with my light texture I can only fade into a specific color. Is there a way I can use a white to transparent gradient texture, so it works with every background color?

    Thanks!
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
  9. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Okay what I get is



    with this code

    Code (CSharp):
    1. Shader "Custom/PointLight" {
    2.  
    3.     Properties {
    4.         _Color ("Color", Color) = (1,1,1,1)
    5.         _LightPosition ("Light position", Vector) = (0,0,0,1)
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.     }
    8.  
    9.     SubShader {
    10.        
    11.         Tags{ "Queue" = "Transparent" }
    12.  
    13.         Pass {
    14.            
    15.         Blend DstColor Zero
    16.  
    17.             CGPROGRAM
    18.  
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #include "UnityCG.cginc"
    22.  
    23.             struct VertexInput {
    24.                 float4 Position : POSITION;
    25.                 float2 TexCoord : TEXCOORD0;
    26.             };
    27.  
    28.             struct VertexOutput {
    29.                 float4 Position : SV_POSITION;
    30.                 float2 WorldPos : TEXCOORD0;
    31.             };
    32.  
    33.             VertexOutput vert(VertexInput i) {
    34.  
    35.                 VertexOutput output;
    36.                 output.Position = mul (UNITY_MATRIX_MVP, i.Position);
    37.                 output.WorldPos = mul(_Object2World, i.Position).xy;
    38.                 return output;
    39.  
    40.             }
    41.  
    42.             uniform sampler2D _MainTex;
    43.             uniform fixed4 _Color;
    44.             uniform float4 _LightPosition;
    45.  
    46.             float4 frag(VertexOutput i) : SV_Target {
    47.  
    48.                 return tex2D(_MainTex, (i.WorldPos - _LightPosition + float2(5,5)) * 0.10) * _Color;
    49.  
    50.             }
    51.  
    52.             ENDCG
    53.  
    54.         }
    55.     }
    56. }
    I'm not sure wich blend type I should use...
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Nothing of note wrong with the shader, but I have no idea how you're generating the texture.

    Try adding a .r after the tex2D()?
     
  11. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    Unfortunately it still doesn't work.

    I generated my texture with gimp.
    Transparent background and a circular gradient from white to transparent.
    Saved it as png.

    Here is the image, but you can't see it because it is white and transparent :p



    And the .r didn't made any difference.

    EDIT: Here are the settings for the texture
     
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    Try this.

    Code (CSharp):
    1. Shader "Custom/PointLight" {
    2.     Properties {
    3.         _Color ("Color", Color) = (1,1,1,1)
    4.         _LightPosition ("Light position", Vector) = (0,0,0,1)
    5.         _LightRange ("Light range", Float) = 10.0
    6.         _MainTex ("Texture", 2D) = "white" {}
    7.     }
    8.     SubShader {
    9.      
    10.         Tags{ "Queue" = "Transparent" }
    11.         Pass {
    12.          
    13.         Blend One One
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #include "UnityCG.cginc"
    18.             struct VertexInput {
    19.                 float4 Position : POSITION;
    20.                 float2 TexCoord : TEXCOORD0;
    21.             };
    22.             struct VertexOutput {
    23.                 float4 Position : SV_POSITION;
    24.                 float2 LightMap : TEXCOORD0;
    25.             };
    26.  
    27.             float _LightRange;
    28.             float4 _LightPosition;
    29.             VertexOutput vert(VertexInput i) {
    30.                 VertexOutput output;
    31.                 output.Position = mul (UNITY_MATRIX_MVP, i.Position);
    32.  
    33.                 float2 worldPos = mul(_Object2World, i.Position).xy;
    34.                 float2 lightRelativePos = worldPos - _LightPosition.xy;
    35.                 output.LightMap = lightRelativePos / max(0.001, _LightRange) * 0.5 + 0.5;
    36.              
    37.                 return output;
    38.             }
    39.             uniform sampler2D _MainTex;
    40.             uniform fixed4 _Color;
    41.             float4 frag(VertexOutput i) : SV_Target {
    42.                 return tex2D(_MainTex, i.LightMap).a * _Color;
    43.             }
    44.             ENDCG
    45.         }
    46.     }
    47. }
    One note, doing lighting this way is a little wrong as a second pass on the already rendered background. The background already has some kind of coloring applied means the additive pass won't have the same result as using an actual light.

    edit: fixed missing "float _LightRange"
     
    Last edited: Oct 9, 2015
  13. stulleman

    stulleman

    Joined:
    Jun 5, 2013
    Posts:
    44
    That works great thanks. And it totally makes sense ;)
    You just missed a uniform float _LightRange, in case someone else uses the code.

    I'm not sure if I understand right what you mean. Can I do something about it?
    And could you give me an advise on how I would achieve that the boxes that aren't lit aren't rendered?

    Thanks!