Jump to content
• Advertisement

# OpenGL Valve techniques in OpenGL

This topic is 2565 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

## Recommended Posts

I read in a valve paper that in hl2 and many other games they used what they call an 'ambient cube' to acheive somewhat global-illumination effects on animated models (for static geometry they used radiosity light mapping). It looked really good, and I'd like to see it in action. Problem is, I'm on a linux computer, and the article used directx with hlsl shaders. Also, they used a specular cube map for rendering of specular highlights. I don't quite understand this one- how do they combine the specular cubemap with the normal map? Also, it wasn't quite clear in the paper- do they use specular cubemapping only with world geometry? Or do they use it for models as well? If not, what do they use for models? (Phong, I would assume)

I looked up these techniques, and found little to no resources covering the topics. At least not in OpenGL.

In short, two valve techniques I'd like to see how to do in OpenGL are ambient cubemapping, and specular cubemapping. If anyone has any resources, code samples, glsl shaders, better alternatives, that would be very helpful. Also, keep in mind I am on an intel 950gma. But that isn't really a problem (I don't think) for these effects.

#### Share this post

##### Share on other sites
Advertisement
Well cube map implies a texture that is 6 faces. So specular is the shininess factor, so if there is a streetlight to the left of the character that is red and a light to the right that is green, the specular would reflect the environment. Same thing with ambient. They might use a stencil cube map for determining visiblity from the sky. So if you are in a narrow path, then only a small part of sky will be visible in the top texture of the cubemap. Which would mean that only normals pointing directly up would receive any ambient light. Maybe you could post the paper.

#### Share this post

##### Share on other sites
The HLSL code should be translatable to GLSL, they don't do anything fancy. For the ambient cube, they just do a weighted lookup based on the normal. I would recommend just going with spherical harmonics instead, which is a more general solution that can provide better quality. For the specular cubemaps, they just compute a reflection vector per pixel based on the normal. You can do this easily with the reflect() intrinsic. To combine this with normal mapping, you just compute the reflection using the normal map normal rather than the interpolated vertex normal. They use the specular cubemaps for dynamic objects, and also have the option of adding analytical specular from a small number of dynamic light sources.

BTW the paper is here: http://www.valvesoftware.com/publications/2006/SIGGRAPH06_Course_ShadingInValvesSourceEngine.pdf

#### Share this post

##### Share on other sites

The HLSL code should be translatable to GLSL, they don't do anything fancy. For the ambient cube, they just do a weighted lookup based on the normal. I would recommend just going with spherical harmonics instead, which is a more general solution that can provide better quality. For the specular cubemaps, they just compute a reflection vector per pixel based on the normal. You can do this easily with the reflect() intrinsic. To combine this with normal mapping, you just compute the reflection using the normal map normal rather than the interpolated vertex normal. They use the specular cubemaps for dynamic objects, and also have the option of adding analytical specular from a small number of dynamic light sources.

BTW the paper is here: http://www.valvesoft...ourceEngine.pdf

I'll look into spherical harmonics; I've heard of them before.

Reading this and another paper (can't find it, title is 'directX tutorial 10, half-life 2 shading'), I still don't quite get the concepts. It seems that they do offline radiosity lightmapping for world geometry, and normal map it. There they have both 'ambient' and diffuse lighting. Statically, that is. Then, they compute specular component based off the nearest specular cube map in the world. Dynamically calculated. Are these various cube maps updated dynamically? And if they are to be normal mapped, then wouldn't the cube map have to be precomputed separately for every surface that has a specular term, using the normals of the surface (provided by a normal map)?

And then we get to model shading. So they approximate radiosity using an ambient cube. Do they use this for the actual ambient component? Or do they use it for diffuse/normal mapping as well? Or do they normal map an ambient component (which doesn't make any sense in my mind)? What exactly do they do? Then there's the specular cube map. The models apparently use the same cube maps as the world, and my questions about the world lighting still remain here.

So, in all, I don't understand really anything of what's going on here. If someone could explain this a bit more clearly and in-depth than the paper, that would be great. Preferably in somewhat of a step-by-step fashion.

#### Share this post

##### Share on other sites
I read in a valve paper that in hl2 and many other games they used what they call an 'ambient cube' to acheive somewhat global-illumination effects on animated models (for static geometry they used radiosity light mapping). It looked really good, and I'd like to see it in action. Problem is, I'm on a linux computer, and the article used directx with hlsl shaders.
The syntax between HLSL and GLSL is almost the same. Replace [font="Courier New"]float4[/font] with [font="Courier New"]vec4[/font] and you're most of the way there.

You can implement an ambient cube either with a 1px cube-map, which you sample using the world-space normal as a texture-coordinate, or you can pass 6 colour values into the shader and add them, weighted by the normal:vec3 colorXP = ...;// +x color vec3 colorXN = ...;// -x color vec3 colorYP = ...;// +y color vec3 colorYN = ...;// -y color vec3 colorZP = ...;// +z color vec3 colorZN = ...;// -z color vec3 ambient = colorXP * clamp( normal_ws.x, 0.0,1.0) + colorXN * clamp(-normal_ws.x, 0.0,1.0) + colorYP * clamp( normal_ws.y, 0.0,1.0) + colorYN * clamp(-normal_ws.y, 0.0,1.0) + colorZP * clamp( normal_ws.z, 0.0,1.0) + colorZN * clamp(-normal_ws.z, 0.0,1.0); 
Then, they compute specular component based off the nearest specular cube map in the world. Dynamically calculated. Are these various cube maps updated dynamically?[/quote]No, the cube-maps aren't dynamic in HL2 (though they could be implemented as to be dynamic).
And then we get to model shading. So they approximate radiosity using an ambient cube. Do they use this for the actual ambient component? Or do they use it for diffuse/normal mapping as well? Or do they normal map an ambient component (which doesn't make any sense in my mind)? What exactly do they do? [/quote]After calculating the surface normal (which may involve reading from a normal-map or not), they use the normal to determine the ambient colour in that direction, as above.

So normal mapping is done first, to determine the actual normal of a pixel.
After that, you can use the normal to find the ambient colour in that direction, and you can also use the normal to calculate the diffuse lighting (using phong/etc for models, or lightmaps for the world), and if required, you can reflect the eye-direction around the normal to calculate a specular reflection direction.
Also, they used a specular cube map for rendering of specular highlights. I don't quite understand this one- how do they combine the specular cubemap with the normal map?[/quote]Convert the tangent-space normal map into a world-space normal, then reflect the eye-direction around this normal to get the reflection direction. Use the reflection direction as a texture-coordinate to sample the cube map.
It seems that they do offline radiosity lightmapping for world geometry, and normal map it. There they have both 'ambient' and diffuse lighting. Statically, that is. [/quote]The lightmapping is quite simmilar to traditional lightmapping, but instead of baking out a single lightmap result, they produce 3 lightmaps for the world.
All the lightmaps are generated without normal-mapping -- only the geometric normals are used.
The first lightmap is generated as if all of the normals were bent slightly in a certain direction (say, slightly north).
The 2nd lightmap is generated as if all of the normals were bent slightly in a different direction (say, slightly south-east).
The 3rd lightmap is generated as if all of the normals were bent slightly in another different direction (say, slightly south-west).

When rendering the world-geometry at runtime, all 3 light-maps are read from, and are mixed together with different weights, which are determined by the normal map.
e.g. If the normal-mapped normal is pointing slightly north, then more weight will be given to the 1st lightmap, or if it's pointing slightly south, more weighting will be given to the 2nd/3rd lightmaps, etc....

#### Share this post

##### Share on other sites

[quote name='keelx' timestamp='1323218743' post='4891287']I read in a valve paper that in hl2 and many other games they used what they call an 'ambient cube' to acheive somewhat global-illumination effects on animated models (for static geometry they used radiosity light mapping). It looked really good, and I'd like to see it in action. Problem is, I'm on a linux computer, and the article used directx with hlsl shaders.
The syntax between HLSL and GLSL is almost the same. Replace [font="Courier New"]float4[/font] with [font="Courier New"]vec4[/font] and you're most of the way there.

You can implement an ambient cube either with a 1px cube-map, which you sample using the world-space normal as a texture-coordinate, or you can pass 6 colour values into the shader and add them, weighted by the normal:vec3 colorXP = ...;// +x color vec3 colorXN = ...;// -x color vec3 colorYP = ...;// +y color vec3 colorYN = ...;// -y color vec3 colorZP = ...;// +z color vec3 colorZN = ...;// -z color vec3 ambient = colorXP * clamp( normal.x, 0.0,1.0) + colorXN * clamp(-normal.x, 0.0,1.0) + colorYP * clamp( normal.y, 0.0,1.0) + colorYN * clamp(-normal.y, 0.0,1.0) + colorZP * clamp( normal.z, 0.0,1.0) + colorZN * clamp(-normal.z, 0.0,1.0);
Also, they used a specular cube map for rendering of specular highlights. I don't quite understand this one- how do they combine the specular cubemap with the normal map?[/quote]Convert the tangent-space normal map into a world-space normal, then reflect the eye-direction around this normal to get the reflection direction. Use the reflection direction as a texture-coordinate to sample the cube map.
Then, they compute specular component based off the nearest specular cube map in the world. Dynamically calculated. Are these various cube maps updated dynamically?[/quote]No, the cube-maps aren't dynamic in HL2 (though they can be implemented as to be dynamic).
And then we get to model shading. So they approximate radiosity using an ambient cube. Do they use this for the actual ambient component? Or do they use it for diffuse/normal mapping as well? Or do they normal map an ambient component (which doesn't make any sense in my mind)? What exactly do they do? [/quote]After calculating the surface normal (which may involve reading from a normal-map or not), they use the normal to determine the ambient colour in that direction, as above.

So normal mapping is done first, to determine the actual normal of a pixel.
After that, you can use the normal to find the ambient colour in that direction, and you can also use the normal to calculate the diffuse lighting (using phong/etc for models, or lightmaps for the world), and if required, you can reflect the eye-direction around the normal to calculate a specular reflection direction.
It seems that they do offline radiosity lightmapping for world geometry, and normal map it. There they have both 'ambient' and diffuse lighting. Statically, that is. [/quote]The lightmapping is quite simmilar to traditional lightmapping, but instead of baking out a single lightmap result, they produce 3 lightmaps for the world.
All the lightmaps are generated without normal-mapping -- only the geometric normals are used.
The first lightmap is generated as if all of the normals were bent slightly in a certain direction (say, slightly north).

The 2nd lightmap is generated as if all of the normals were bent slightly in a different direction (say, slightly south-east).

The 3rd lightmap is generated as if all of the normals were bent slightly in another different direction (say, slightly south-west).

When rendering the world-geometry at runtime, all 3 light-maps are read from, and are mixed together with different weights, which are determined by the normal map.
e.g. If the normal-mapped normal is pointing slightly north, then more weight will be given to the 1st lightmap, or if it's pointing slightly south, more weighting will be given to the 2nd/3rd lightmaps, etc....
[/quote]

Wow that cleared up everything. Thanks for the explanation!

Also, I must say I'm astounded by the ingeniousness here...

#### Share this post

##### Share on other sites

• Advertisement
• Advertisement

• ### Popular Contributors

1. 1
2. 2
3. 3
Rutin
16
4. 4
5. 5
A4L
12
• Advertisement

• 11
• 26
• 10
• 11
• 9
• ### Forum Statistics

• Total Topics
633723
• Total Posts
3013541
×

## Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!