World coordinates for texture on procedural mesh

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!

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: Unity - Manual: ShaderLab built-in values

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 :smile:
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 :smile:

Shader "Custom/PointLight" {

    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _LightPosition ("Light position", Vector) = (0,0,0,0)
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader {
      
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct VertexInput {
                float3 Position : POSITION;
                float2 TexCoord : TEXCOORD0;
            };

            struct VertexOutput {
                float4 Position : POSITION0;
                float2 WorldPos : TEXCOORD0;
            };

            VertexOutput vert(VertexInput i) {

                VertexOutput output;
                output.Position = mul (UNITY_MATRIX_MVP, i.Position);
                output.WorldPos = i.TexCoord;
                return output;

            }
      
            uniform float4 _Color;
            uniform sampler2D _MainTex;
            uniform float2 _LightPosition;

            float4 frag(VertexOutput i) : SV_Target {

                float2 position = i.WorldPos - _LightPosition;
                float dist = sqrt(dot(position, position));

                float3 mix = lerp(_Color, float4(0,0,0,0), saturate(dist));
                return float4(mix, 1.0f);

            }

            ENDCG

        }
    }
}

It probably doesn’t make any sense.
Can you give me a hint where I have to do what?

Thanks!

output.WorldPosition = mul(_Object2World, input.Position).xy;

Okay so now I have this:

Shader "Custom/PointLight" {

    Properties {
        //_Color ("Color", Color) = (1,1,1,1)
        //_LightPosition ("Light position", Vector) = (0,0,0,1)
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader {
       
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct VertexInput {
                float3 Position : POSITION;
                float2 TexCoord : TEXCOORD0;
            };

            struct VertexOutput {
                float4 Position : POSITION0;
                float2 WorldPos : TEXCOORD0;
            };

            VertexOutput vert(VertexInput i) {

                VertexOutput output;
                output.Position = mul (UNITY_MATRIX_MVP, i.Position);
                output.WorldPos = mul(_Object2World, i.Position).xy;
                return output;

            }

            uniform sampler2D _MainTex;

            float4 frag(VertexOutput i) : SV_Target {

                return tex2D(_MainTex, i.WorldPos);

            }

            ENDCG

        }
    }
}

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.

On vertex input the position should be a float4. On output it should be to SV_POSITION, not POSITION0.

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!

Okay what I get is

with this code

Shader "Custom/PointLight" {

    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _LightPosition ("Light position", Vector) = (0,0,0,1)
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader {
       
        Tags{ "Queue" = "Transparent" }

        Pass {
           
        Blend DstColor Zero

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct VertexInput {
                float4 Position : POSITION;
                float2 TexCoord : TEXCOORD0;
            };

            struct VertexOutput {
                float4 Position : SV_POSITION;
                float2 WorldPos : TEXCOORD0;
            };

            VertexOutput vert(VertexInput i) {

                VertexOutput output;
                output.Position = mul (UNITY_MATRIX_MVP, i.Position);
                output.WorldPos = mul(_Object2World, i.Position).xy;
                return output;

            }

            uniform sampler2D _MainTex;
            uniform fixed4 _Color;
            uniform float4 _LightPosition;

            float4 frag(VertexOutput i) : SV_Target {

                return tex2D(_MainTex, (i.WorldPos - _LightPosition + float2(5,5)) * 0.10) * _Color;

            }

            ENDCG

        }
    }
}

I’m not sure wich blend type I should use…

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()?

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 :stuck_out_tongue:

And the .r didn’t made any difference.

EDIT: Here are the settings for the texture

Try this.

Shader "Custom/PointLight" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _LightPosition ("Light position", Vector) = (0,0,0,1)
        _LightRange ("Light range", Float) = 10.0
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
     
        Tags{ "Queue" = "Transparent" }
        Pass {
         
        Blend One One
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct VertexInput {
                float4 Position : POSITION;
                float2 TexCoord : TEXCOORD0;
            };
            struct VertexOutput {
                float4 Position : SV_POSITION;
                float2 LightMap : TEXCOORD0;
            };

            float _LightRange;
            float4 _LightPosition;
            VertexOutput vert(VertexInput i) {
                VertexOutput output;
                output.Position = mul (UNITY_MATRIX_MVP, i.Position);

                float2 worldPos = mul(_Object2World, i.Position).xy;
                float2 lightRelativePos = worldPos - _LightPosition.xy;
                output.LightMap = lightRelativePos / max(0.001, _LightRange) * 0.5 + 0.5;
              
                return output;
            }
            uniform sampler2D _MainTex;
            uniform fixed4 _Color;
            float4 frag(VertexOutput i) : SV_Target {
                return tex2D(_MainTex, i.LightMap).a * _Color;
            }
            ENDCG
        }
    }
}

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”

That works great thanks. And it totally makes sense :wink:
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!