3
\$\begingroup\$

I've written a vertex/fragment shader for my game. It uses 3 textures and represents a floating water on the wall (so I have 1 texture for water, 1 for geometry and the last one is mask) so it looks like this:

enter image description here

But I also need a spot light attached to the camera to lit my geometry, and this is the single dynamic light source on the stage.

Well, I can use a surface shader to take dynamic lighting into attention, but I want to avoid using them because: it will be much larger and its execution will be much slower. Besides, I need only one dynamic light source, so I can (can I?) just calculate its lighting in the shader, and pass a position/direction to the shader using material.SetVector(). It is something like:

float4 frag(FragmentInput i) : COLOR0
{
    float4 col = i.col * tex2D(_Texture, i.uv);
    col.a = tex2D(_AlphaMask, i.uv).r / 2;

    if(col.a > 0.25f) // it's water, just return the scrolled texture color
    {
        float4 l1 = tex2D(_WaterTexture, float2(i.uv.x, i.uv.y - _Time.y / 16));
        float4 l2 = tex2D(_WaterTexture, float2(i.uv.x, i.uv.y - _Time.y / 8));
        return float4((l1 * l2).rgb, 0.5f);
    }
    else // it's geometry calculate spot light lighting
    {
        float lit = // ... calculations here ...
        return col * lit;
    }
}

Is it possible to do this? Will it work faster than the surface shader?

\$\endgroup\$
2
\$\begingroup\$

What you want to do is definitely possible, since Unity does it in the built-in shaders, and there's nothing in those that you can't duplicate. I can't say whether doing it yourself result in a faster shader than what Unity would generate from a surface shader.

Have a look at Unity's shader utility code, which includes macros to help you access the lighting information from your shaders. You'll should find these in the Unity install tree (C:\Program Files (x86)\Unity\Editor\Data\CGIncludes on my Windows machine, /Applications/Unity/Unity.app/Contents/CGIncludes on my Mac). The file that's relevant to your goal is AutoLight.cginc.

You'd need to include AutoLight.cginc in your shader and then use the macros in there to do the work. Looks like you'll want to:

  • Add LIGHTING_COORDS(idx1, idx2) to your fragment input
  • Call TRANSFER_VERTEX_TO_FRAGMENT(a) in your vertex shader
  • Call LIGHT_ATTENUATION(a) in your fragment shader to get the light level
  • Use _LightColor0 to get the light's color

(Note 0: The idx1 and idx2 parameters are indexes of two TEXCOORD semantics.)

(Note 1: In the macro calls above, the parameter is the fragment input structure.)

(Note 2: I haven't used spotlights in Unity, so it's fairly likely that you'll have to debug my approach a little.)

Hope this helps!

\$\endgroup\$
  • \$\begingroup\$ Thanks. I repeat, I need only one light source (the spotlight) to affect my geometry. How can I process only spot and do not process any other light sources that can be closer to the object than the spotlight? \$\endgroup\$ – Netherwire Feb 22 '14 at 5:51
  • 1
    \$\begingroup\$ I see, so there are several light sources in your scene? There are more lights that just "the single dynamic light source on the stage?" Your question would be clearer if you edited it to indicate this. \$\endgroup\$ – Victor T. Feb 27 '14 at 23:27
0
\$\begingroup\$

As Netherwire implied in reply to my previous answer, the real question seems to be "How can I make an object respond to only one of several light sources in my scene?"

That's answered here, in Unity's own community answer system:

http://answers.unity3d.com/questions/8646/light-affecting-only-specific-objects.html

In this case, you would need to assign the wall object to its own layer, then edit each non-spot light source so that the light's Culling Mask does not include the wall object's layer.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.