//Set up texture units
//Unit 0 - decal texture
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex->GetId());
//Set up texture shader for unit 0:
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
//unit 1 - pass through atten coords:
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_PASS_THROUGH_NV);
//unit 2 - bump (normal) map - currently not in use
glActiveTextureARB(GL_TEXTURE2_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, bump->GetId());
//unit 3 - normalization cube map - currently not in use
glActiveTextureARB(GL_TEXTURE3_ARB);
glEnable(GL_TEXTURE_CUBE_MAP_ARB);
glBindTexture(GL_TEXTURE_CUBE_MAP, normal_cube_tex->GetId());
glActiveTextureARB(GL_TEXTURE0_ARB);
//Set up register combiners
//one general combiner
glCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
glCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, light_diffuse);
// I've seen attenuated bump mapping examples without decal textures;
// I've seen ppl attenuation with flat textured surfaces...
// ...but never seen bump_map + decal + ppl_attenuation, with the
// exception of Ron Frazier's papers, which describe 6(!!!)-pass method.
//
// Furthermore, that method doesn't use any mid-end (GF3/GF4) features
// like vp's, more TMU's, more RC stages...
//
// The following code does only attenuated ppl with decal.
// How to *modify* it, so attenuated bump mapping WITH decal texture
// can be done in a single-pass? or, at least, 2 passes?
// Is that possible with GF3/GF4 hardware?
// Combiner 1 does: tex1 dot tex1 -> spare0
// Spare0 now holds the squared length of the vector in tex1.
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE1_ARB,
GL_EXPAND_NORMAL_NV, GL_RGB);
glCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_TEXTURE1_ARB,
GL_EXPAND_NORMAL_NV, GL_RGB);
glCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);
// Set up final combiner to output: clampto01(1 - spare0) * col0 * tex0.
// First do (tex0 * col0) in the EF multiplier:
glFinalCombinerInputNV(GL_VARIABLE_E_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_F_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
// Now do rest:
glFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_INVERT_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
glEnable(GL_REGISTER_COMBINERS_NV);
glEndList();
// Any ideas?
//
// Thanks for advance,
//
// Dmitry aka Freelancer.
I think most people don’t find your question embedded in the middle of the code.
No, I don’t know of a way for GeForce3/4 to do BOTH decal AND attennuation in a single pass.
If you can get attennuation in there, then you can accumulate lighting into the frame buffer, and then modulate decal when you’re done. That’ll tint specular, unless you accumulate specular seperately, though.
So if your inputs are:
tex0 - Decal Texture
tex1 - Bump-map
tex2(cube) - Light Dir
tex3(cube) - Half angle
and the incomming color is the distance from the light to the fragment divided by the range. (don’t worry about clamping as values outside this range are black)
With the above you can do simple 1-D^2 attenuation per-pixel with decal, bumpmapping and specular in one pass.
A, B and C are vertices; the light is at A. D is a point in the middle of the edge CB.
Suppose “B” and “D” are both further away from A than “range”. However, “D” is closer than “range”. No amount of per-vertex magic is going to give you an interpoland that will make the light affect D, but not C or B.
Ok I stand corrected, there are some corner cases where what I said would not work accuratly (ie extremly poorly tessilated surfaces with lights that have a range less than the length of a polygon. (very rare in my work)).
So use this to handle jwattes’ case:
So if your inputs are:
tex0 - Decal Texture
tex1 - Bump-map
tex2(cube) - Light Dir
tex3 - Light distance divided by range
will give you attenuation per-pixel with decal and bumpmapping in all cases. (if you want a crude pp specular you can hack it by using the secondary color to pass a half angle) But since your origional question did not ask for specular, this is ok.
I think FULL ammount of ppl goodies (diffuse+specular bump+attenuation) cannot be done on GF3/GF4 because of 4-textures-at once limitation. Thus, it can be done with Radeons (6 texturea-at-once).
For future: does anyone have a cool ARB_fragment_program that does if ALL in a single pass?
// eintrittspunkt für das vertex programm
void main (
// attribute
in float4 position : ATTR0,
in float3 tangent : ATTR1,
in float3 binormal : ATTR2,
in float3 normal : ATTR3,
in float2 tex_coord : ATTR4,
//matrizen
uniform float4x4 model_view,
uniform float4x4 model_view_IT,
uniform float4x4 model_view_projection,
uniform float4x4 model_view_I,
uniform float4 l_position[3],
// für das fragment programm
out float4 o_position : POSITION,
out float2 o_tex_coord0 : TEXCOORD0,
out float3 o_view_vec : TEXCOORD1,
out float3 o_eye_position : TEXCOORD2,
out float3 o_light_vec0 : TEXCOORD3,
out float3 o_half_vec0 : TEXCOORD4,
out float3 o_light_vec1 : TEXCOORD5,
out float3 o_half_vec1 : TEXCOORD6,
out float3 o_light_vec2 : TEXCOORD7,
out float3 o_half_vec2 : COLOR0
)
// vertex in clip space
o_position = mul(model_view_projection,position);
// und in eye space für attentuation im fragment shader
o_eye_position = mul ( model_view, position);
// matrix um vom objekt space in den tangen space zu kommen
// nur für richtungsvektoren !!
float3x3 obj_to_tan = float3x3(tangent,binormal, normal);
// view vektor in tangent space
// position des auges in object space
float4 obj_position_eye = mul(model_view_I, float4(0.0f,0.0f,1.0f,1.0f));
float3 obj_V = normalize((obj_position_eye - position).xyz );
o_view_vec = normalize(mul(obj_to_tan, obj_V));
#ifdef LIGHT0
// position der lichtquelle in object space
float4 obj_light_position0 = mul(model_view_I, l_position[0]);
// berechnung von L in objekt space
float3 obj_L0 = ((obj_light_position0 - position).xyz);
//und transformation von L in tangent space und ausgabe an den fragment shader
o_light_vec0 = (mul(obj_to_tan,obj_L0)); #endif
#ifdef LIGHT1
// position der lichtquelle in object space
float4 obj_light_position1 = mul(model_view_I, l_position[1]);
// berechnung von L in objekt space
float3 obj_L1 = normalize((obj_light_position1 - position).xyz);
//und transformation von L in tangent space und ausgabe an den fragment shader
o_light_vec1 = normalize(mul(obj_to_tan,obj_L1)); #endif
#ifdef LIGHT2
// position der lichtquelle in object space
float4 obj_light_position2 = mul(model_view_I, l_position[2]);
// berechnung von L in objekt space
float3 obj_L2 = normalize((obj_light_position2 - position).xyz);
//und transformation von L in tangent space und ausgabe an den fragment shader
o_light_vec2 = normalize(mul(obj_to_tan,obj_L2)); #endif
// damit man beim compilieren eventuell einzelne lichter abschalten kann #define LIGHT0
//#define LIGHT1
//#define LIGHT2
// funktion für point light
// man beachte das alle positionen / normalen im selben koordinatensystem sind
// addiert lichtintensität zur eingabe hinzu !!
void point_light (
// ein /ausgabe
inout float4 diffuse,
inout float4 specular,
in float3 light_vec,
in float3 half_vec,
in float3 normal,
in float3 light_attenuation,
in float distance,
// material
in float material_shininess,
// vormultiplizierte farben
in float4 k_ambient,
in float4 k_diffuse,
in float4 k_specular
)
I have nothing to add; I think all I’ve said is exactly true.
Regarding per-pixel vs per-vertex lighting: those “special cases” are actually surprisingly common. And the light distance can be longer than the side of a triangle; all that’s necessary is for one point on a edge to be closer to the light than the vertices at either end of the edge; that’s pretty common (but with very high tesselation, it’s usually less noticeable).
Both methods are valid and useful; I just answered exactly the question that was asked, not another question. (Although at times I find myself doing that, too