Is there a way I can get at lights and their properties inside a shader?
I’m playing around with my own lighting calculations and I’d like to get light information (light color, light position, etc.) inside the shader so that I can calculate the light falling on an object based on my own lighting model. It didn’t seem obvious to me how to get at that kind of information in the shader.
I suspect that it is through properties, but that would mean I need to create a javascript script that ‘talks’ to my shader (passing the lighting information to the shader), no? Or am I missing something obvious?
All the pixel lit shaders run a pass for each light, accumulating the results. So in the shader it makes sense to access the “current light”. Some variables in UnityCG.cginc expose the current light properties:
_ObjectSpaceLightPos[0] is the object space light position, also look at ObjSpaceLightDir function and the way it’s used in builtin shaders.
_LightColor0, _ModelLightColor[0] and _SpecularLightColor[0] are the colors.
Other than that, UnityCG.cginc contains some unused stuff at the moment, so not everything you see in there is actually useable. I’ll clean it up in the future.
I’m assuming you don’t mean ‘pass’ in the Shaderlab/CG sense of the word, right? So if a shader has multiple passes itself, then all those passes get executed for each pixel light, right?
Also, what about vertex lights? How do I access those?
Yes it does. Multipass pixel lights start to be expensive really quickly.
Vertex lights are calculated using fixed function OpenGL lighting. So if you have a vertex shader, you automatically don’t have vertex lights. In a pixel shader, you can access interpolated diffuse and specular lighting colors using COLOR0 and COLOR1 input semantics (look at struct v2f_vertex_lit and VertexLight function in UnityCG.cginc).
Ok, so now I’m a bit confused. Let’s say I have a point light in my scene. It’s defined as a ‘vertex light’, which I’m assuming would normally be used by the fixed pipeline to interpolate the shading of the polygons of the object. Those color values, as you say, I can get at in the pixel/fragment shader through the COLOR0 and COLOR1 semantics.
Now let’s say I want my own vertex lighting scheme. Let’s say I have a different lighting model. I don’t want to use the fixed pipeline, but rather my own vertex shader. In my vertex shader I’ll need access to all the lights in my scene (which, in my example, is one point light, but it could be more). I sort of have this working using the following:
// first I define my light structure
Light light;
// then in my vertex shader I get the light position
float3 lightPosition = light.GetLightPosition(0).xyz;
float3 lightColor = light.GetLightColor(0).xyz;
// then I create my ambient, emissive, diffuse, specular terms
// and add them up to create my final color
// I pass that result on to the pixel/fragment shader
Is this how this should work? If so, how do I get the number of lights in my scene so I can properly loop through all the lights to see if they affect my object?
It’s not possible to use custom vertex program with vertex lights at the moment.
The reasons are many: many video cards do not support loops in the shaders at all (they have to be unrolled with a known number of iterations). Fitting 8 lights (OpenGL vertex light limit) into lower vertex program models with loop unrolling is almost impossible, unless your lighting is much simpler than the fixed function one.
So if you want custom per-vertex interpolated lighting, you still have to use “pixel lights” as far as Unity is concerned. You just calculate lighting in a vertex program, and interpolate results to the fragment program. It is still a costly one pass per light though (*).
(*) I’ve seen the code in Unity that seems to support more than one light in a pixel light pass, that would help in your case. Never used it myself though, let me experiment or better bug Nicholas about it
Ok, a quick look says the following: a pixel-lit Pass can have a LightCount tag that says how many simultaneous pixel lights can it handle (up to 2 currently). Then Unity will render as much lights simultaneously. If there are less lights than the limit, the others will still be rendered, but their colors will be set to black.
So what you do is: put a LightCount tag in your pixel lit pass, and access up to that amount of lights in _ObjectSpaceLightPos array and the other arrays. And you always process this fixed amount of lights (if there will be less, the colors will be black). A quick example (works, but not for production use):
Shader "Test" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0.5)
_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
}
Category {
Blend AppSrcAdd AppDstAdd
Fog { Color [_AddFog] }
SubShader {
Pass {
Tags {
"LightMode" = "Pixel"
"LightCount" = "2"
}
CGPROGRAM
// profiles arbfp1
// vertex vert
#include "UnityCG.cginc"
#include "AutoLight.cginc"
// fragmentoption ARB_fog_exp2
// fragmentoption ARB_precision_hint_fastest
inline float3 MyObjSpaceLightDir( in float4 v, int index )
{
return _ObjectSpaceLightPos[index].xyz - v.xyz * _ObjectSpaceLightPos[index].w;
}
struct v2f {
V2F_POS_FOG;
float4 color : COLOR0;
};
v2f vert (appdata_base v)
{
v2f o;
PositionFog( v.vertex, o.pos, o.fog );
float4 color = float4(0,0,0,0);
for( int i = 0; i < 2; ++i )
{
float3 lightDir = normalize(MyObjSpaceLightDir( v.vertex, i ));
float c = dot( v.normal, lightDir );
color += c * _ModelLightColor[i];
}
o.color = color;
return o;
}
ENDCG
SetTexture [_MainTex] {combine primary}
}
}
}
}
Note that vertex lights will not be rendered by this pass, so you still need to provide some sort of vertex lit passes (or use high enough PixelLightCount in quality settings :))
Sweet. That’s the ticket. One of the main problems I had was that I was originally setting the Lightmode to vertex - thinking that I was doing vertex lighting. But I guess that actually means the fixed function ‘vertex lighting’.
Thanks Aras! That’s great! Having more information on how Unity talks to a shader, is to me, quite important. If there is a shader already written out there and all I need to do is figure out how to make it work in Unity it saves me a lot of time (I don’t have to reinvent the wheel).
The things that I’ve needed to know as I’ve started learning shaders and Cg are how they fit into Unity: what matrices are available for transforms (what are they called and how are they mapped to other common naming schemes), how do I get at light information, how do I get at camera or object information in the shader, how are parameters passed from JavaScript to CgPrograms, how does Unity access the fixed pipeline and how does that jive with the programmable pipeline, basically knowing what I can do in Unity that I can do with other shader environments like RenderMonkey or FXComposer.
It would be neat to see a section on what to do when doing only fixed pipeline work, a section on a purely programmatic setup and a section on one that combines them both.
As I’m learning this stuff it doesn’t really matter to me that some cards are able or not able to do what I’m trying to do (or for that matter, that I can do something easier using a fixed pipeline method), I can tackle that later. But to begin with it’s good to know what I can do in Unity - how much can I push her. Also, when I learn, I like to understand what’s under the hood - so being able to programatically reproduce what the fixed pipeline is doing is a great learning tool. Then, when production shaders are needed I’ll consider looking at performance, fallbacks and fixed pipeline methods.
One of the things I really want Unity to do is shadows. I don’t care if they are imperfect or that they sometimes fail and look weird, or that some cards won’t be able to handle them (for those cards I’ll use a blob shadow fallback) - I really want dynamic shadows (at least for one light and one character). If I can get that, then I think Unity is ready for my game. Hence my push toward a shadow map shader. I’m almost there… I have a shader that does projection mapping so I have one more step to shadow maps (thanks for helping me with that, by the way).
Anyway, the docs that are already there are definitely useful - and the link to the dynamically updated ones are great! Thanks again.