Attenuated PPL + ..... = ?

Here is the code:

void SetPassThroughAttenStates()
{
static GLuint att_list = 0;

//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.

}

If anyone interested, here is the accompanying vertex program:

!!ARBvp1.0

#-------------------------------------------------------------------------#

Attenuation Vertex Program - vp for per-vertex (attenuated) ppl setup

#-------------------------------------------------------------------------#

Parameters’ definition:

Matrices:

PARAM tm[4] = { state.matrix.texture[0] }; # texture matrix zero, contains atten. params
PARAM mvp[4] = { state.matrix.mvp }; # modelview-projection matrix

Lighting:

PARAM lightPos = program.env[0]; #object space light position

Per vertex inputs:

ATTRIB iPos = vertex.position; # position
ATTRIB iCol0 = vertex.color; # primary color
ATTRIB iTex0 = vertex.texcoord; # texture coord, unit 0
ATTRIB iNorm = vertex.normal; # normal
ATTRIB iTang = vertex.attrib[4]; # tangent
ATTRIB iBnrl = vertex.attrib[5]; # binormal

Outputs:

OUTPUT oPos = result.position; # position
OUTPUT oCol0 = result.color; # color
OUTPUT oTex0 = result.texcoord; # texture coords for decal texture, unit 0
OUTPUT oTex1 = result.texcoord[1]; # texture coords for pp attenuation, unit 1
OUTPUT oTex2 = result.texcoord[2]; # texture coords for pp attenuation, unit 1
OUTPUT oTex3 = result.texcoord[3]; # tangent space light vector for ppl, unit 2

TEMP vertToLight;

Transform vertex position to clip space:

DP4 oPos.x, mvp[0], iPos;
DP4 oPos.y, mvp[1], iPos;
DP4 oPos.z, mvp[2], iPos;
DP4 oPos.w, mvp[3], iPos;

Just pass on texture coordinates:

MOV oTex0, iTex0;
MOV oTex2, iTex0;

Transform position by texture matrix 0 to get 3d tex coords for attenuation;

Put (s,t,r) -> (s1, t1, r1) for pass-through attenuation algorythm

If you use it, be sure to rip out all the attenuation stuff,

and use FP/RC for fragment processing.

DP4 oTex1.x, tm[0], iPos;
DP4 oTex1.y, tm[1], iPos;
DP4 oTex1.z, tm[2], iPos;

light vector = (light position - vertex position)

ADD vertToLight, lightPos, -iPos;

Convert light vector to tangent space

DP3 oTex3.x, vertToLight, iTang;
DP3 oTex3.y, vertToLight, iBnrl;
DP3 oTex3.z, vertToLight, iNorm;

Pass on input color:

MOV oCol0, iCol0;

END

EDIT: texture matrix is set up the following way:

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(0.5f, 0.5f, 0.5f);
glScalef(0.5f, 0.5f, 0.5f);
glScalef(1.0f / lightRadius, 1.0f / lightRadius, 1.0f / lightRadius);
glTranslatef(-light_position[0], -light_position[1], -light_position[2]);
glMatrixMode(GL_MODELVIEW);

[This message has been edited by Freelancer (edited 02-08-2004).]

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.

It is fairly easy to do bump + decal + pp_attenuation + pp_specular on Geforce 3 level hardware.

See this thread for attenuation: http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/008916.html

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.

That’s not per-pixel attennuation.

Consider:

B
/|
A D
|
C

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.

Maybe a bit of <code> something would do the magic? these ascii arts are a bit confusing if they dont come out the way intended to result…

Heeya, im back, hehehee!
Michael

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.

Your move jwatte.

Thank you guys, but…

  1. 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).

  2. For future: does anyone have a cool ARB_fragment_program that does if ALL in a single pass?

Guys, I really need it, SOS!!!

It is inCG, I leave it as an exercise to compile it to ARBfp :wink:

It does the full (bumped) lighting with attenuation and gloss mapping… not optimized and please don’t mind my native languages comments…

vertex program

/******************************************************************************
*

  • transformiert die L und V vektoren in den tangent space und gibt sie in
  • textur koordinaten an den fragment shader für bumpmapping

******************************************************************************/
#define LIGHT0
//#define LIGHT1
//#define LIGHT2

// 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
    ) 

{

// texturkoordinaten weiterschieben
o_tex_coord0 = tex_coord;

// 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

}

fragment program

/******************************************************************************
*

  • bumpmapping, mit diffuse und specular licht

******************************************************************************/

// 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
  			)

{
// lichtberechnung
float4 dots = lit ( dot(normal,light_vec), dot(normal,half_vec),material_shininess);

float3 dist_vect = { 1.0f, distance, distance*distance };
float attenuation = 1.0f / dot(light_attenuation, dist_vect);

// ausgabe der farben
diffuse+= attenuation * (k_ambient + dots.y * k_diffuse);
specular+= attenuation * k_specular* dots.z;
}

float3 expand (float3 v)
{
return (v-0.5f)*2.0f;
}

// eintrittspunkt für das vertex programm

void main (
in float2 tex_coord : TEXCOORD0,

  	in	float3	view_vec				: TEXCOORD1,
  	in	float3	eye_position			: TEXCOORD2,
  				
  	in	float3	light_vec0				: TEXCOORD3,
  	in	float3	half_vec0				: TEXCOORD4,

  	in	float3	light_vec1				: TEXCOORD5,
  	in	float3	half_vec1				: TEXCOORD6,

  	in	float3	light_vec2				: TEXCOORD7,
  	in	float3	half_vec2				: COLOR0,
  	
  	
  	// zum z-test, blending ...
  	out	float4 o_color						: COLOR,
  	


  	//licht & material
  	uniform	float3		l_attenuation[3],
  	uniform	float4		l_position[3],
  			
  				
  	uniform	float4		k_ambient[3],
  	uniform	float4		k_diffuse[3],
  	uniform	float4		k_specular[3],
  	
  	uniform	float		m_shininess[3],
  	
  	// texturen
  	uniform sampler2D	tex_base,
  	uniform sampler2D	tex_normal,
  	uniform sampler2D	tex_height,
  	uniform sampler2D	tex_gloss

    ) 

{

float4 diffuse_sum = {0.0f,0.0f,0.0f,0.0f};
float4 specular_sum= {0.0f,0.0f,0.0f,0.0f};

float3 normal = normalize(expand( tex2D(tex_normal, tex_coord).xyz));

// lichtquelle 0
#ifdef LIGHT0
float3 L0 = normalize(light_vec0);
float3 H0 = normalize( L0 + normalize(view_vec) );

point_light ( diffuse_sum,
specular_sum,

  			L0,
  			H0,
  			
  			normal,
  			
  			l_attenuation[0],
  			distance(l_position[0].xyz,eye_position),	
  			m_shininess[0],
  			
  			k_ambient[0],
  			k_diffuse[0],
  			k_specular[0]
  		);

#endif

// lichtquelle 1
#ifdef LIGHT1
float3 L1 = normalize(light_vec1);
float3 H1 = normalize( L1 + normalize(view_vec) );

point_light ( diffuse_sum,
specular_sum,

  			L1,
  			H1,
  			
  			normal,
  			
  			l_attenuation[1],
  			distance(l_position[1].xyz,eye_position),	
  			m_shininess[1],
  			
  			k_ambient[1],
  			k_diffuse[1],
  			k_specular[1]
  		);

#endif

// lichtquelle 2
#ifdef LIGHT2
float3 L2 = normalize(light_vec2);
float3 H2 = normalize( L2 + normalize(view_vec) );

point_light ( diffuse_sum,
specular_sum,

  			L2,
  			H2,
  			
  			normal,
  			
  			l_attenuation[2],
  			distance(l_position[2].xyz,eye_position),	
  			m_shininess[2],
  			
  			k_ambient[2],
  			k_diffuse[2],
  			k_specular[2]
  		);

#endif

// ausgabe der farben
float4 base = tex2D(tex_base, tex_coord);
float4 gloss = tex2D(tex_gloss, tex_coord);
o_color.rgb = diffuse_sum.rgb * base.rgb + specular_sum.rgb * gloss.rgb;
o_color.a = base.a;

}

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 :slight_smile: