Why is ambient light at half strength?

I read something about this on the forum somewhere recently, but didn’t understand what it meant. Now, I get what’s going on, but I don’t know why it’s going on. Why does this shader make an object 50% grey, instead of white, if you set Ambient to white in the Render Settings? Diffuse and specular light don’t work like this.

SubShader 
{
	Lighting On
	
	Material
	{
		Ambient (1,1,1)
	}
	
	Pass {}
}

Ambient light gets halved because the standard Unity way to apply lighting is to double dynamic lights, but not ambient light. However, since vertex lighting is done all at once, the ambient term is halved Ahead of time to cancel out the doubling.

Why? That seems like an arbitrary decision that didn’t need to get made.

Indeed that was a decision made a lot of time ago, but I do think it makes sense.

By doubling the result of any lighting calculations, you can have “overbright” lights if you want to. That is, with a bright light, a texture can be brightened as a result. Without adding a “double!” thing, your textures can only get darker.

Of course, all that is a poor man’s “HDR”, but does not require any special hardware support and is almost free for performance.

Is the use of DOUBLE kind of made obsolete by Light.intensity? Or is the vertex lighting result clamped to 1.0?

Yes, the lighting is clamped. As such, I don’t see how making the Ambient calculation half or what it “should be” is anything but nonsensical - I don’t see how what Aras is talking about has anything to do with things, really. I don’t know anything about the programmable pipeline yet, but I do know that in ShaderLab, if you want “overbrightness”, you need to use DOUBLE or QUAD in the texture combiners.

(As it is now, you can still get overbright lights by just using the ambient calculation. You just need to set it at (2,2,2) or above. If the Ambient lighting calculation wasn’t halved, then you could just use (.5,.5,.5) instead, use DOUBLE later, and get the results that you get now.)

It does make sense. By defaulting to 50% anyone using Unity can increase the brightness of the scene by increasing the ambient light.

If ambient light started at 100% your only option would be to darken your scene. You’d be a little screwed, production-wise, if you want to quickly brighten it.

What?

Edit: OH! I believe you’re thinking about the color in Render Settings. That’s not what this thread is about. I’m totally cool with that setting! :smile:

Like I said, I know basically nothing about the programmable pipeline, but if the non-Cg portion of the default Vertex Lit shader was written like this, I think it would make sense. Light intensity could be at 1 instead of .5 to achieve texture brightness at 1, and light intensity could be brought up to 2 for double-highlights. When you create a light, Unity could set its intensity at 1 instead of the current .5. I mean, why .5? That seems pretty arbitrary to the user, methinks.

The only problem with this is that the emissive color will be twice as bright as expected. I don’t see a way to fix that unless the Ambient setting isn’t halved as it is.

Shader "Vertex Lit" {

Properties
{
	_Color ("Main Color", Color) = (1,1,1)
	_SpecColor ("Spec Color", Color) = (1,1,1)
	_Emission ("Emmisive Color", Color) = (0,0,0)
	_Shininess ("Shininess", Range (0.01, 1) ) = 0.7
	_MainTex ("Base (RGB)", 2D) = "white"
}

SubShader
{
	Lighting On
	SeparateSpecular On
	
	Pass
	{
		Material
		{
			Diffuse (.5,.5,.5)
			Ambient (1,1,1)
			Shininess [_Shininess]
			Specular [_SpecColor]
			Emission [_Emission]
		}
		
		SetTexture [_MainTex]
		{
			ConstantColor [_Color]
			Combine texture * constant
		}
		
		SetTexture [_nothing]
		{
			Combine previous * primary DOUBLE
		}
	}
}

}

Edit: Looking over this, and pondering that Emissive issue again, I believe I understand why the decision may have been made. ShaderLab doesn’t provide you the ability to multiply the variables in the Material block. What we really could use is the ability to do so. It would look something like this:

Material
{
	Diffuse [Color] * .5
	Ambient [Color]
	Shininess [_Shininess]
	Specular [_SpecColor]
	Emission [_Emission] * .5
}

Is it possible that the language could be expanded to include that?

Here’s my guess at why it is the way it is:

Unity originally didn’t have Light.intensity. The only way you could control brightness was by changing the colour of a light.

When making a decent-looking lighting framework, you need to double your lighting result in the fixed function pipeline. If you don’t, the brightest light can only ever give you lighting results of 1, and so all you can do is make your objects darker than their base colour/texture.

Doubling lighting isn’t a big deal. Most lights are point lights, and the falloff of a point light means that you tend to have to have an object pretty close before you notice the results of a clamped lighting equation. That is, most objects will be darker than the light colour because of attenuation. Even though you’re doubling the result, you mostly don’t see the blow-out that means “really bright”. This means that most people don’t notice that Unity’s lighting is doubled, and that’s Ok because it just looks good.

Ambient light, on the other hand, doesn’t have attenuation. It’s always at full brightness. If it were doubled, users would notice and it wouldn’t make sense to most people. Since Unity doubles the lighting result, and because you can’t separate ambient from directional lighting in the fixed function Phong equation, it made sense to halve ambient light first so that users saw the same brightness going into ambient as they saw coming out.

The one place where it was obvious that Unity doubled lighting and it looked weird was with directional lights. Because they don’t attenuate, a new directional light would always look really bright when it was set to white. Since it would be much harder to make a special case for directional lights vs. point and spot lights, that quirk just got left as it was.

With the addition of Light.intensity, point and spot lights start with an intensity of 1, whereas directional lights start with an intensity of 0.5, making them more reasonable when you first create one. Most people don’t notice this at first and it doesn’t matter, because directional lights just feel right now.

So that’s probably why Unity does lighting the way it does. It’s not perfect. It might make sense to give ambient light an intensity too, and set it to 0.5 and let that be the default halving instead of putting it into the fixed function pipeline. However, hindsight is 20/20, and you can’t go changing the fixed function pipeline now. Aside from the initial confusion when you start writing shaders and find your ambient term halved, I don’t think the current setup is a problem.

I don’t know that I’d say that. I don’t really use the builtin shaders, but the ones that I have seen, double the non-ambient calculation. I know that most people aren’t into writing their own shaders, but the lighting calculation doesn’t need to be doubled if you don’t want it to be.

That’s what I want too, but I’d like it done the way that it is done in my last code snippet. I mean, what about QUAD? Just because you can use DOUBLE doesn’t mean that taking it into consideration for ambient is the way to go. Why not multiply it by .25, use QUAD in all the builtin shaders, and then let us have really bright highlights?! :smile:

This is arbitrary, the results don’t make sense because of the decision, and it doesn’t provide control for those of us who want it.

What I’ve been saying in this thread is that they don’t. .5 = 1 makes brains melt. :x

That sounds overcomplicated. Adding the addition to ShaderLab that I mentioned should solve the problem more cleanly. Most users don’t need to deal with this stuff, I’d wager.

I don’t see why not. I have no idea how Unity works internally, but from this point forward, Unity could make whatever check is necessary to find out if a game was built with an old version, and add the * .5 into the material settings for ambient light wherever necessary. New versions of Unity could ship with the Ambient setting not being halved, but the shaders would be updated. People can deal with whatever problems this would cause in their old custom shaders - an automatic fix option from UT would be nice, however.

Unity has changed things for the better several times before, requiring code updates. I believe in embracing the improvements, not obsolete, nonsensical stuff, just for the sake of backwards compatibility.

Strength of directional light = 1, and it’s white. No ambient light is involved. The shader uses DOUBLE or QUAD to allow for super-blown highlights. There is no way to adjust the diffuse and ambient settings to have different colors, and still have the highlights not be blown. That’s unintuitive to the user, and I call that a problem. (And as I am primarily a user of Unity iPhone, directional lights are important to me, due to the performance increase they allow.)

This reminds me of the issue with the audio volume control in Unity using a linear taper instead of an exponential one. Just because something is easy for the computer to deal with, doesn’t mean you should do it. Make the numbers make sense to human beings.

Have to agree with Jessy.

There is really no to very little sense to half it upfront.
Its not like modulatex2 / modulatex4 combiners are costly on hardware at all (at least not on the gaming targeted 3D API DX where it has been a standard hardware feature since TnL / DX7 days).

Might be that it once made sense to do this with OpenGL, but I really don’t care about what OpenGL does wrong or does not support that has been a standard for soon a decade and I don’t feel that the correct approach is to enforce “OpenGL stupidity” onto all users, especially as programable pipeline is the standard today and having an engine that thinks of itself as god and plain simply ignores the input you give it to use is not really productive, logical nor desireable (potentially not even acceptable for some who want(ed) to use unity).

If I send in A as data to be used and don’t tell it to modify it through some other flag, I expect it to use A and A only, not what it thinks “A should be”

dreamora, I think you misunderstand the topic. This issue has nothing to do with OpenGL/D3D.

In any case, if you think Unity’s current behaviour is a serious issue, put it on Uservoice and see what happens. You’re right that the current implementation is not perfect. That said, aside from confusion when you first start writing shaders and the unintuitive default value of 0.5 for directional light intensity, I really don’t see how this is a practical problem.

Nobody is going to know what the heck I am talking about, as per usual. :slight_smile: I’ll reserve my votes for something that will actually make sense and benefit everybody to a more noticeable degree. :wink:

Edit: I don’t even know what I could suggest there, other than what I wrote at the end of this post. If you can’t do that with ShaderLab, then I think that the current implementation is a reasonable compromise of evil. :evil:

I don’t think it’s a practical problem either. As an artist, I can just put in my head "Unity acts dumb and I need to set all my light intensities at half of what they should be. Lighting can largely be a matter of “use the slider until it looks good”, but that’s not good enough for me, as an artist and a user. I want the numbers to mean something - it allows me to create faster and comprehend my own work better.

As far as I know, we’re clipped to 4x brightness of the texture (using QUAD), but within that range, it would be nice to have artistic control over how bright something can get. As it is, you have two choices: your ambient light can make something half value, full value, or double value, and the rest of your lights can do twice that. Exactly. Nothing in between. What if I want my desert warrior to have blown out highlights that are 3.15x full value?

So, I’ve never written a programming language, and I don’t know much about GPU hardware. Can the ShaderLab addendum I proposed be included, or not? (It would be awesome to be able to multiply a Color variable by a Range one. This is something that’s useful to artists, and might actually be something a few people would vote for on UserVoice. But I’m not going to do that if it’s technically impossible.)

I don’t think the extension you’re looking for is necessary. You can set Color values outside [0,1] via script:

using UnityEngine;
using System.Collections;

[AddComponentMenu("Tests/SetAmbient")]

public class SetAmbient : MonoBehaviour {
	public Color ambient;
	
	protected void Start() {
		RenderSettings.ambientLight = RenderSettings.ambientLight*2f;
	}
}

I’m guessing that RenderSettings.ambientLight is halved by the property’s set function, and nothing special is done in Shaderlab or anywhere else.

As for your desert soldier example, I’m not sure I follow.

I did not know you could have color components be greater than 1 like that. Thanks! You could even do this via a Menu Item, for instance, and view your shaders without having to hit Play first. However, that’s a poor solution in terms of usability. So, necessary, no, but it certainly detracts from what attracted me to Unity in the first place, which is ease-of-use.

Okay, first of all, this badass is a warrior, not a solider. He answers to neither man nor god, friend. :shock:

Now, we’ve already discussed how diffuse light is kind of complicated, and how it’s not incredibly important that the numbers make sense, because in most cases, you’re just eyeballing things anyway. And if you’re using something other than a directional light that doesn’t attenuate, then who really cares about the numbers anyway? But emission is a much simpler affair - basically the same thing as ambient. But wait! Emission is not halved like ambient is!

Imagine that you painted a texture with “maximized contrast”, and you want to be able to control how overbright it can get. So if all you’re using is emissive light, using the built-in shaders, you have to set Emission to 50% grey in order to get back the texture as you painted it. That doesn’t make any sense. But if you set the Emissive to white, and multiply that by a slider that ranges between 1 and 2, or 1 and 4, then voila, you have an overbrightness slider than makes sense to humans!

(Of course, if you wanted the numbers to make sense, you’d have to multiply the slider value by .5 or .25 first, so the Material block would have to allow more than two operands.)

(By the way, the desert warrior is a totally hypothetical character. And I realized that it was also a poor illustration for emissive light. :roll:)

It seems to me that what you’re after isn’t a job for Shaderlab, but a Unity inspector issue. Modulating uniform values isn’t something you should do in a shader (and it’s not a feature of the fixed function pipeline). If you could set colours in the inspector that had elements outside [0,1], your example would just work with basic colour inputs.

I’ve never seen a color picker that extended past white. And it wouldn’t work, because the numbers are still off. It doesn’t matter what your color picker range is. If you want overbright lighting, then you’re going to have to set Emission in the grey to get your texture at default brightness. That is, unless ShaderLab can do what I suggested.

Providing colour values outside [0,1] lets you cancel out the halving of the global ambient term. Colour pickers can’t do that, but you can do it with a script.

Shaderlab is the wrong place for the feature you seem to want. Shaderlab doesn’t do anything special with the global ambient term. It’s just that the global ambient term is one half of what it appears in the inspector. You can do this with any other colour you want via scripting.

Lighting needs to be doubled, otherwise it looks bland. If you want your emissive colour to be half as bright, reduce its brightness by half.

I believe that is exactly what I am suggesting.

I don’t agree with that, and I tried to illustrate that with the Emission example. If I set my Emission color to white, then I expect to get my texture back exactly as I painted it. As it is, you need to set _Emission to 50 % grey to achieve that, instead. A color picker with extended range won’t change that.

I want a three part multiplication. Let’s continue with the Emission example. I want the first operand to be the color you set for emission. I want the second operand to be a value that negates the result of your DOUBLE or QUAD addition, so the value will be either .5 or .25. Lastly, I want the third operand to be a “Range” which uses values between either 1-2, if your use DOUBLE, or 1-4, if you use QUAD, which functions as an overbrightness control. At the left end, you can’t overbrighten textures. at the right end, you can make them either 2x or 4x as bright. That’s not an ideal level of control, but I believe it’s the best we can get in the fixed function pipeline.

Picking colors from a script is bad usability.

ShaderLab is an interface to use Materials. Materials are the nice, “artistic” way of making things looks good in Unity. Doing this stuff via script is a terrible idea.

Again, that works, but the slider means nothing intuitive. None of this is a problem with possibility. It’s very much a problem with usability.

Shaderlab is a shader specification language. It exposes features of graphics hardware in a platform-agnostic way. Pre-multiplying material and lighting values by arbitrary coefficients is not a feature of fixed-function hardware, and it doesn’t belong in Shaderlab.