Has anybody had any success using this function, or seen a working example?
I’ve been trying/struggling to use it to approximately light a particle system (as they don’t natively support lightprobes).
But the values I’m getting out of it don’t seem to match up to the SH values used by the shaders in the ShadeSH9 function.
I’ve got a shader that just writes out the constant term (unity_SHAr.w, unity_SHAg.w, unity_SHAb.w ), and I’m trying to match this up to the differently-ordered coefficients from GetInterpolatedLightProbe(). I thought that the first 3 coefficients would be the ones I needed.
But at the moment, I can’t even get this simplest term to match up… there’s no values that come close to the output of the shader (and it’s not just an out-by-factor-of-2 issue, already considered that one…)
Does Unity do further calculations on these coefficients before reshuffling them into shader parameters??
I’ve discovered that GetInterpolatedLightProbe() does not add any dynamic SH light, including ambient light - this was contributing to my problems, as the scene had a non-zero ambient light.
But after removing the ambient, the best that I’ve managed to achieve is this:
But it’s still some way away from working properly. It looks closer than it really is, as to I’ve got a ‘magic number’ divide-by-two, which shouldn’t be necessary, as the shader doesn’t do it. And if I reduce both the shader and the script version to just display the constant term, there’s quite some difference - I’ve not managed to get those to match up yet. (Also, in other scenes with different light probe setups, the difference in colour/brightness is much more severe)
Surely somebody has figured this out, and managed to light a particle system reasonably well using lightprobes?
Ah, thanks for that - looks like it explains my problems, I’d (incorrectly) assumed that the values returned by GetInterpolatedLightProbe would be ready to feed straight into the shader, but it looks they need a bit of processing first - will give that a try!
Based on the code from that doc, I’ve now got a solution that works quite nicely. Might be useful to anyone else trying to do the same thing.
(Would be nicer if particle effects had some support for lightprobes natively, though, or if Unity had a built in function to sample a lightprobe and return an RGB value like this…)
// -----------------------------------------------------------------------------------------------
// LightProbeUtil
//
// Samples a lightprobe using LightmapSettings.lightProbes.GetInterpolatedLightProbe, giving an
// RGB value for a given world space position and normal.
//
// Useful for lighting particle effects.
// -----------------------------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class LightProbeUtil
{
static float[] aSample = new float[27]; // SH sample consists of 27 floats
static Vector4[] avCoeff = new Vector4[7]; // SH coefficients in 'shader-ready' format
static Vector3 vRGB = new Vector3();
static float s_fSqrtPI = (float)Mathf.Sqrt(Mathf.PI);
static float fC0 = 1.0f / (2.0f*s_fSqrtPI);
static float fC1 = (float)Mathf.Sqrt(3.0f) / (3.0f*s_fSqrtPI);
static float fC2 = (float)Mathf.Sqrt(15.0f) / (8.0f*s_fSqrtPI);
static float fC3 = (float)Mathf.Sqrt(5.0f) / (16.0f*s_fSqrtPI);
static float fC4 = 0.5f * fC2;
// ------------------------------------------------------------------------------------------
// Sample light probes at a given world-space point for a given world-space normal
// Returns an RGB value
// ------------------------------------------------------------------------------------------
public static Vector3 SampleLightProbes( Vector3 vPos, Renderer r, Vector3 vNormal3 )
{
Vector4 vNormal;
vNormal.x = vNormal3.x;
vNormal.y = vNormal3.y;
vNormal.z = vNormal3.z;
vNormal.w = 1.0f;
if ( LightmapSettings.lightProbes!=null )
{
// Sample the probes
LightmapSettings.lightProbes.GetInterpolatedLightProbe( vPos, r, aSample );
// Convert this sample into 'shader-ready' coefficients
// (See 'Stupid SH Tricks' doc by Peter-Pike Sloan, code at the very bottom of the doc)
// ------------------------------------------------------------------------------------------
for ( int iC=0; iC<3; iC++ )
{
avCoeff[iC].x =-fC1 * aSample[iC+9];
avCoeff[iC].y =-fC1 * aSample[iC+3];
avCoeff[iC].z = fC1 * aSample[iC+6];
avCoeff[iC].w = fC0 * aSample[iC+0] - fC3*aSample[iC+18];
}
for ( int iC=0; iC<3; iC++ )
{
avCoeff[iC+3].x = fC2 * aSample[iC+12];
avCoeff[iC+3].y = -fC2 * aSample[iC+15];
avCoeff[iC+3].z = 3.0f * fC3 * aSample[iC+18];
avCoeff[iC+3].w = -fC2 * aSample[iC+21];
}
avCoeff[6].x = fC4 * aSample[24];
avCoeff[6].y = fC4 * aSample[25];
avCoeff[6].z = fC4 * aSample[26];
avCoeff[6].w = 1.0f;
// Calculate the RGB value, in the same way as 'ShadeSH9' in the shaders
// ------------------------------------------------------------------------------------------
// Linear and constant polynomial terms
vRGB.x = Vector4.Dot( avCoeff[0], vNormal );
vRGB.y = Vector4.Dot( avCoeff[1], vNormal );
vRGB.z = Vector4.Dot( avCoeff[2], vNormal );
// 4 of the quadratic polynomials
Vector4 vB;
vB.x = vNormal.x*vNormal.y;
vB.y = vNormal.y*vNormal.z;
vB.z = vNormal.z*vNormal.z;
vB.w = vNormal.z*vNormal.x;
vRGB.x += Vector4.Dot( avCoeff[3], vB );
vRGB.y += Vector4.Dot( avCoeff[4], vB );
vRGB.z += Vector4.Dot( avCoeff[5], vB );
// Final quadratic polynomial
float fC = vNormal.x*vNormal.x - vNormal.y*vNormal.y;
vRGB.x += fC * avCoeff[6].x;
vRGB.y += fC * avCoeff[6].y;
vRGB.z += fC * avCoeff[6].z;
// Add the ambient, as that isn't in the probes
vRGB.x += RenderSettings.ambientLight.r;
vRGB.y += RenderSettings.ambientLight.g;
vRGB.z += RenderSettings.ambientLight.b;
return vRGB;
}
return Vector3.one;
}
// ------------------------------------------------------------------------------------------
// Sample light probes at a given world-space point using the world up normal
// Returns an RGB value
// (Faster, simplified version, for lighting things like small particle effects)
// ------------------------------------------------------------------------------------------
public static Vector3 SampleLightProbesUp( Vector3 vPos, Renderer r )
{
if ( LightmapSettings.lightProbes!=null )
{
// Sample the probes
LightmapSettings.lightProbes.GetInterpolatedLightProbe( vPos, r, aSample );
// Convert this sample into 'shader-ready' coefficients
// (Simplified for the case of a 0,1,0 normal)
// ------------------------------------------------------------------------------------------
for ( int iC=0; iC<3; iC++ )
{
avCoeff[iC].y =-fC1 * aSample[iC+3];
avCoeff[iC].w = fC0 * aSample[iC+0] - fC3*aSample[iC+18];
}
avCoeff[6].x = fC4 * aSample[24];
avCoeff[6].y = fC4 * aSample[25];
avCoeff[6].z = fC4 * aSample[26];
// Calculate the RGB value, in the same way as 'ShadeSH9' in the shaders
// (Simplified for the case of a 0,1,0 normal)
// ------------------------------------------------------------------------------------------
vRGB.x = avCoeff[0].y + avCoeff[0].w;
vRGB.y = avCoeff[1].y + avCoeff[1].w;
vRGB.z = avCoeff[2].y + avCoeff[2].w;
vRGB.x -= avCoeff[6].x;
vRGB.y -= avCoeff[6].y;
vRGB.z -= avCoeff[6].z;
// Add the ambient, as that isn't in the probes
vRGB.x += RenderSettings.ambientLight.r;
vRGB.y += RenderSettings.ambientLight.g;
vRGB.z += RenderSettings.ambientLight.b;
return vRGB;
}
return Vector3.one;
}
}
Now, pass the lightprobe to a shader that samples it per pixel with the particle’s normal (usually sphere normals or a normal map) to create very accurately lit particles. Yay.
Hey, just wondering if anyone has successfully used this method with Unity 5.
I have tried both bluescrn’s and Bas Smit’s solutions (changing the coefficient indexes over to the new SphericalHarmonicsL2[int, int]), with some success, but the results differ significantly from ShadeSH9 in a shader.
Here are some examples (using a “toon” shader which samples the light in only two directions):
The first row of images uses ShadeSH9 in a shader, and the second row uses Bas Smit’s method.
Assuming this method was working previously (and I don’t have Unity 4 pro, so I can’t check), I can think of two likely reasons this is happening:
a) Unity 5 encodes the light probes differently.
b) The SH coefficients aren’t in the order I think they are. In altering Bas Smit’s script, I just changed:
cs[0] to cs[0, 0]
cs[1] to cs[0, 1]
cs[2] to cs[0, 2]
cs[3] to cs[1, 0]…
My code doesn’t add ambient, you can try setting the ambient to black. Also, Unity adds realtime lights to the data from the probes at runtime so disable all lights after baking the probes. Please let us know that fixes it.
Thank you, however changing the ambient light and disabling lights didn’t seem to change anything. All of my lights were set to baked only, and I’m fairly sure that ambient light is now baked into the probes, being sort of bounced into the scene from some giant sphere surrounding it, which makes sense as a way to do it.
Hmm ok, I’ll have to port this to unity 5 soon, I’ll post here when I get it to work. That said, it seems like the color is correct, but the intensity is off, bit of a stab in the dark but can you try multiplying the result by 2?
From left to right: shader SH9; your code (x2); your code (x10) just to be sure.
The intensities are different, but to my eye it also seems like the shader version is getting a fair green tinge from the light bouncing off the floor, where the other versions don’t. This makes me think perhaps it’s sampling in the wrong direction? (I’ll try just flipping the vector coordinates around a bit)
Good luck porting the code over, and thanks for the help.
Did you guys manage to get this working in Unity 5? I’ve got something close enough but I’m not sure that it’s “correct”. I had to flip the X and Y of my normal vector when sampling for some reason, but not Z.
It seems like baking lightprobes is currently simply broken, these are the result of a single directional light in 4 and 5. Clearly a single light should never light more than half a sphere. You can vote for the issue here