GL matrix story (stupid but true)

Discuss programming topics for the various GPL'd game engine sources.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

GL matrix story (stupid but true)

Post by Baker »

I am writing code to force portrait display mode by rotating the frustum/ortho/viewport.

Results in a 90 turn just by inserting ...

glRotatef (90, 0, 0, 1); // Add me!
glFrustum (-xmax, +xmax, -ymax, -ymax, glnear, glfar);

Stupidly, OpenGL fails to do this! Possibly due to a division by zero I am guessing where perhaps -xmax and xmax are used somewhere in the matrix calculation in a way they add together to be zero.

Hilarously ... instead I use mh's glmatrix frustum calculation which performs the calculation in a way that avoids division by 0 in common situations --- then do glLoadMatrixf (m) to load that matrix.

Then I live in "Not my problem!" city!
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: GL matrix story (stupid but true)

Post by Spike »

well, (-ymax)-(-ymax) == 0, so yeah, what do you expect?
I really hope that was a typo in your post and not your code. :P

glFrustum internally does a glMultMatrix, so make sure your prior matrix is sane. Also that you've got GL_MATRIX_MODE set properly. too much state sucks. but yeah, glLoadMatrix is the most sane way to do most of this anyway. The matrix generated by the function is well defined and well documented so implementing your own should be fairly easy, and more efficient on account of using glLoadMatrix instead of the implicit glMultMatrix of glFrustum.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: GL matrix story (stupid but true)

Post by Baker »

Spike wrote:well, (-ymax)-(-ymax) == 0, so yeah, what do you expect?
I really hope that was a typo in your post and not your code. :P
Twas a typo. That'd be a very small vertical frustum otherwise :biggrin:
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: GL matrix story (stupid but true)

Post by mh »

Yeah, the great thing about OpenGL is that you can look at the spec, read how things are implemented, then construct your own variant if you need to.

It should be possible (and easy!) to write a GL_RotatedFrustum that does the same in a single operation, not that it would be a performance improvement (unless for some reason you're calling glFrustum thousands or more times per frame).

None of these calls do anything special or magic or voodoo. They just construct an ordinary 4x4 matrix, do a multiplication and load the result to the GPU. There's nothing mysterious going on in their internals.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

Since we're talking about GL matricies, could please tell me, you, engine gurus, if it's possible to create an ortographic matrix to pass to FTE shadowmap code to make it project dynamic parallel shadows? Because I tried and I tried but so far I only created weird static plane shadows! :biggrin:

Note that I created same matrix also in GLSL and pass it to the shadow map fragment shader part

PS:sorry to have sneaked into your post with a question, Baker, but the opportunity was too greedy!! :biggrin:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: GL matrix story (stupid but true)

Post by Spike »

toneddu2000, you're actually looking for two projection matricies.
the one used to render the shadowmap needs to transform from camera space to clip space.
the one used to read the shadowmap needs to transform again from camera space, but this time to the texture coords (with depth).

clip space in opengl ranges from -1 on the left, bottom, and nearest part of the screen, to 1 on the right, top, and furthest part of the screen.
on the other hand, texture coords range from 0 to 1, as does the 'red' component of the shadowmap texture (aka the depth).
sampling from a 2d shadow sampler can be done with a 3d texcoord - the 3rd coord being a reference value to compare the texture against. This gives slightly smoother results. the alternative is to treat it as a regular texture and do the compare yourself, which may be needed with gles2, but mneh.

so the first matrix is a standard ortho matrix, while the second one can be generated from the first by multiplying it against a bias matrix, but if you care about precision you'd get more precise results by calculating it directly.
and don't forget the divide-by-w thing, though I don't recall it actually being needed with ortho matricies.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

thanks a lot Spike, I'll reply in the original thread, to avoid spoling Baker's thread
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: GL matrix story (stupid but true)

Post by Baker »

Doesn't bother me in the slightest, just fyi. Was just sharing a short experience.

Your post about shadowmapping was interesting. Carry on ... should you wish ...
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

Lol, thanks Baker. Anyway, I couldn't get it work. Spike advices were very clear but probably my opengl skills are too low to create a working orthographic projection in FTE for project parallel shadows.

I adapted this GLSL shader that mimic parallel light and I used this site as reference for bias matrix and for learning how shadows are applied in fragment shader

This is the modification in Sh_GenShadowMap() in gl_shadow.c. I simply delete all the code regarding omni/spot and replaced with this. Just temp code.

Code: Select all

float left = -1;
float bottom = -1;
float znear = -1;
float right = 1;
float top = 1;
float zfar = 1;
Matrix4x4_CM_Orthographic(r_refdef.m_projection_std,left,right,bottom,top,znear,zfar);
memcpy(r_refdef.m_projection_view, r_refdef.m_projection_std, sizeof(r_refdef.m_projection_view));
In CSQC_UpdateView I created a simple light

Code: Select all

ltest = dynamiclight_add(cam.origin,cvar("render_light_radius"),[cvar("render_light_intensity"),cvar("render_light_intensity"),cvar("render_light_intensity")],0,"",0);
This is the GLSL shader created by Hugh Kennedy (MIT license)

Code: Select all

!!permu FOG
!!samps diffuse
!!cvarf render_sun_ground
!!cvarf render_sun_x
!!cvarf render_sun_y
!!cvarf render_sun_z
!!cvarf render_sun_back_x
!!cvarf render_sun_back_y
!!cvarf render_sun_back_z
!!cvarf render_sun_intensity
#include "sys/defs.h"
#include "sys/pcf.h"
#include "sys/fog.h"
#define shdwIntensity 0.1
varying vec2 tc;
varying vec3 eyevector,normalvector,lightvector;
varying vec4 modelvector,viewvector,vtexprojcoord,shadowCoord ;
uniform float cvar_render_sun_x,cvar_render_sun_y,cvar_render_sun_z,cvar_render_sun_ground;
uniform float cvar_render_sun_back_x,cvar_render_sun_back_y,cvar_render_sun_back_z,cvar_render_sun_intensity;
//Copyright (c) 2014 Hugh Kennedy - MIT License - https://github.com/hughsk/glsl-directional-light
//adapted to FTE engine by Antonio "toneddu2000" Latronico - antoniolatronico.com
#ifdef VERTEX_SHADER
void main (void)
{
	vec3 eyeminusvertex = e_eyepos - v_position.xyz;
	normalvector = -v_normal.xyz;
	eyevector.x = dot(eyeminusvertex, v_svector.xyz);
	eyevector.y = dot(eyeminusvertex, v_tvector.xyz);
	eyevector.z = dot(eyeminusvertex, v_normal.xyz);
	lightvector = l_lightposition - v_position.xyz;
	const float right = 1.0;
    const float bottom = -1.0;
    const float left = -1.0;
    const float top = 1.0;
    const float far = 1.0;
    const float near = -1.0;
	mat4 shdwmat = mat4(
        vec4(2.0 / (right - left), 0, 0, -(right + left) / (right - left)),
        vec4(0, 2.0 / (top - bottom), 0, -(top + bottom) / (top - bottom)),
		vec4(0, 0, -2.0 / (far - near), -(far + near) / (far - near)),
		vec4(0, 0, 0, 1)
    );
	mat4 biasmat = mat4(
        vec4(0.5, 0.0, 0.0, 0.0),
        vec4( 0.0, 0.5, 0.0, 0.0),
		vec4( 0.0, 0.0, 0.5, 0.0),
		vec4(0.5, 0.5, 0.5, 1.0)
    );
	mat4 depthMVP = m_projection * m_view * m_model;
	mat4 depthBiasMVP = biasmat*depthMVP;
	vtexprojcoord = (shdwmat*biasmat*vec4(1.0,1.0,1.0, 1.0));
	shadowCoord = depthBiasMVP * vec4(v_position.xyz, 1.0);
	gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
vec3 directional_light(vec3 normal,vec3 light,vec3 surface,vec3 lightDirection,mat4 modelMatrix,mat4 viewMatrix,vec3 viewPosition,float shininess,float specularity)
{
	vec3 direction = normalize((vec4(lightDirection, 1.0)).xyz);
	vec3 halfDirection = normalize(direction + viewPosition);
	vec3 tNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz);
	float diffuse = max(dot(tNormal, direction), 0.0);
	float halfDot = max(dot(tNormal, halfDirection), 0.0);
	float specular = max(pow(halfDot, shininess), 0.0);
	return max(light * (diffuse * surface + diffuse * specular * specularity), vec3(0.0));
}

vec3 directional_light(vec3 normal,vec3 light,vec3 surface,vec3 lightDirection,mat4 modelMatrix,mat4 viewMatrix,vec3 viewPosition)
{
	vec3 direction = normalize((modelMatrix * vec4(lightDirection, 1.0)).xyz);
	float diffuse = max(dot(normal, direction),shdwIntensity);
	return max(light * diffuse * surface,vec3(0.0));
}

void main (void)
{
	vec3 sky = vec3(0.510,0.247,0.000);
	vec3 gnd = vec3(0.002,0.486,0.912);
	vec3 direction1 = normalize(vec3(cvar_render_sun_x,cvar_render_sun_y, cvar_render_sun_z));
	vec3 direction2 = normalize(vec3(cvar_render_sun_back_x, cvar_render_sun_back_y, cvar_render_sun_back_z));
	vec3 surface = vec3(1.0,1.0,1.0);
	vec3 color = vec3(0.3,0.3,0.3);
	//realtime shadows
	float visibility = 1.0;
	if ( ShadowmapFilter(s_shadowmap, shadowCoord)  <  shadowCoord.z){
		visibility = 0.2;
	}
	#ifdef GREENBRIGHT
		color = vec3(0.059,0.750,0.000);
	#endif
	#ifdef GREENDARK
		color = vec3(0.000,0.510,0.003);
	#endif
	#ifdef GREENYELLOW
		color = vec3(0.484,0.840,0.032);
	#endif
	#ifdef BROWNBRIGHT
		color = vec3(0.795,0.391,0.027);
	#endif
	#ifdef BROWNDARK
		color = vec3(0.545,0.267,0.001);
	#endif
	#ifdef GREYBRIGHT
		color = vec3(0.645,0.655,0.622);
	#endif
	#ifdef GREYDARK
		color = vec3(0.507,0.515,0.489);
	#endif
	#ifdef GREYVERYDARK
		color = vec3(0.369,0.375,0.356);
	#endif
	#ifdef RED
		color = vec3(0.990,0.188,0.000);
	#endif
	#ifdef YELLOW
		color = vec3(0.990,0.700,0.016);
	#endif	
	vec3 composite = mix(surface,color,max(dot(normalvector, direction1),shdwIntensity));
	color *= vec3(cvar_render_sun_intensity);
	#ifdef SHDW
	color = color + (visibility * l_lightcolour );
	#endif
	vec3 lightsun = vec3(1.000,0.878,0.000);//light sun (usually bright sun)
	vec3 lightambient = vec3(0.081,0.593,0.685);//light gi (usually dark blue)
	mat4 matrixModel = m_projection;
	mat4 matrixView = m_invviewprojection;
	vec3 lighting = 
	directional_light(normalvector, lightsun, color, direction1, matrixModel, matrixView, eyevector)
	+ directional_light(normalvector, lightambient, color, direction2, matrixModel, matrixView, eyevector);
	gl_FragColor = fog4(vec4(lighting,1.0));
	gl_FragDepth = gl_FragCoord.z;
}
#endif
And this is the material with the BEMODE for rtlights. Note that diffusemap it's useless, since GLSL colors meshes by itself,it's there only to avoid FTE complaining about missing pass

Code: Select all

parallelshdw
{
	//first set textures
	diffusemap textures/env/green.tga
	//second bemode for realtime lights
	bemode rtlight
	{
		program ton_lightparallel#SHDW
	}
}
This will make a nice diffuse parallel light with a counter ambient light, which it's cool.Look how round light is.
Image

And this is the shader with the #SHDW flag... practically nothing useful! :)
Image
As you can see, shadow is just a square sitting in the middle of scene. It's like it was placed at specific coordinates

Weird thing is that, even with FTE release without my modification, rendering is the same as above
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: GL matrix story (stupid but true)

Post by Spike »

for a start, you're not even using the ortho matrix in your glsl, at least not in any way that would prevent it from being optimised out.

your light is in one place and its projecting onto some meshes. the vertex coords get multiplied by the model matrix which gives you the vertexes world coord. at this point things diverge - you have both a camera (with its view + perspective-projection matrix), and a light (which has its own orientation/view matrix, and its own ortho-projection matrix).
gl_Position needs the screen/camera position in clip space.
however, your shadowCoord needs the shadow coord in texture space upon your projected texture. this has absolutely nothing to do with the camera's view or projection matrix - the only normal matrix that matters for this varying is the model matrix.

what I'd recommend you try is to first just get your texture projection working.
ditch the shadow map for now, and just project a regular coloured 2d image onto a black wall for now.
that way you'll know when your s+t texture coords are correct, that there's no weird affine issues, etc, in a way that is completely separate from the shadowmap generation.
divide and conquer. you won't know when the p coord is correct until you reinstate the actual shadowmap, but at least you'll know that you're actually reading the right part of the shadowmap. the actual depth values themselves are probably the last thing you should tackle (ignoring stuff like pcf/smoothing/etc).
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

Spike wrote:what I'd recommend you try is to first just get your texture projection working.
ditch the shadow map for now, and just project a regular coloured 2d image onto a black wall for now.
Something like that?
Image

I projected this texture
on this scene.
Image
I used same matrix trasformations for both gl_Position and projected texture coords.
It's important to mention that, in FTE, if you want to assign to gl_Position, something that is not ftetranform(), you have to use z margin to "extrude" it a little, otherwise zfighting will ruin the rendering
Here's the code

Code: Select all

#include "sys/defs.h"
varying vec4 projectedCoords;
#ifdef VERTEX_SHADER
void main (void)
{
	projectedCoords = m_projection * m_view * m_model* vec4(v_position.xyz, 1.0);
	//projection in clip space
	gl_Position = m_projection * m_view * m_model* vec4(v_position.xyz, 1.0);
	//z-fighting fix copied from here: https://stackoverflow.com/questions/24142068/z-fighting-solutions-in-depth-test-in-opengl-how-do-they-work
	float Z = 1.0; 
	float far = 2000.0;   
	gl_Position.z = 2.0*log(gl_Position.w*Z + 1.0)/log(far*Z + 1.0) - 1.0;
	gl_Position.z *= gl_Position.w;
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
void main (void)
{
	vec4 projectimg = texture2DProj(s_t0,normalize(projectedCoords));
	gl_FragColor = projectimg;
}
#endif
Of course, now, inclination of texture is tied to camera inclination.
Was that what you meant? Now what? :biggrin:
Should I use, instead of camera inclination, an arbitrary matrix for rotation?
This is fun :biggrin:
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: GL matrix story (stupid but true)

Post by Spike »

no, you want to project the texture, not sample it from screen space. If you'd wanted that then there are easier ways to do it. Ones that do not require you creating a z-fighting minefield. Try re-reading the second block of text in my previous post.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

ok, this is what I did so far. I premise that I use this page as reference(for those interested, go to "9.3 Projective Texturing" chapter).

I don't sample anymore from screen space but I use model matrix multiplied by the light projection matrix (engine-builtin l_cubematrix matrix)

vertex shader(same for both "versions").

Code: Select all

void main (void)
{
	texCoordProj = m_model* l_cubematrix * vec4(v_position.xyz, 1.0);
	gl_Position = ftetransform();
}
fragment shader

Code: Select all

uniform sampler2D s_t0;
void main (void)
{
	vec2 uv = normalize(gl_FragCoord.xy/gl_FragCoord.w)/2.0;
	vec4 tex = texture2D(s_t0,uv);
	vec4 projectimg,texCoordProjNorm;
	texCoordProjNorm = normalize(texCoordProj);	
	projectimg = texture2DProj(s_t0,texCoordProjNorm);
	gl_FragColor = projectimg;
}
And I ended up with this.
Image
As you can see, texture is projected but scale is very low. I put in the top-right corner a zoomed view of texture.
This one was the texture2DProj version. Now the other one.

The next one uses .st swizzle of texCoordProjNorm and texture2D instead using the full vec4 coord and texture2DProj,

Code: Select all

uniform sampler2D s_t0;
void main (void)
{
	vec2 uv = normalize(gl_FragCoord.xy/gl_FragCoord.w)/2.0;
	vec4 tex = texture2D(s_t0,uv);
	vec4 projectimg,texCoordProjNorm;
	texCoordProjNorm = normalize(texCoordProj);	
	projectimg = texture2D(s_t0,texCoordProjNorm.st);
	gl_FragColor = projectimg;
}
so probably it's not what I should do, but, weirdly, final image is exactly what I wanted to achieve!
Image
Now texture orientation is not tied anymore to camera inclination and it seems that texture "covers" entire scene with same texture coordinates. But I used texture2D, and not texture2DProj

adding to vertex shader a bias matrix and multiplying it to model matrix and l_cubematrix, makes image less "warped"

Code: Select all

mat4 biasmat = mat4(
        vec4(0.5, 0.0, 0.0, 0.0),
        vec4( 0.0, 0.5, 0.0, 0.0),
		vec4( 0.0, 0.0, 0.5, 0.0),
		vec4(0.5, 0.5, 0.5, 1.0)
    );
	texCoordProj = m_model* l_cubematrix * biasmat* vec4(v_position.xyz, 1.0);
Image

So, did I do things correctly? Is it right to use texture2D, instead of texture2DProj? Or was the texture2DProj approach the correct one(but I missed some steps)? Or none of them? :biggrin:


Then, I used another approach from this page, which it uses projection matrix multiplied by model matrix

Code: Select all

!!ver 130
#include "sys/defs.h"
varying vec4 texCoordProj;
#ifdef VERTEX_SHADER
void main (void)
{
	mat4 biasmat = mat4(
        vec4(0.5, 0.0, 0.0, 0.0),
        vec4( 0.0, 0.5, 0.0, 0.0),
		vec4( 0.0, 0.0, 0.5, 0.0),
		vec4(0.5, 0.5, 0.5, 1.0)
    );
	texCoordProj = m_projection* m_model* biasmat* vec4(v_position.xyz, 1.0);
	gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
void main (void)
{
	vec2 uv = normalize(gl_FragCoord.xy/gl_FragCoord.w)/2.0;
	vec4 tex = texture2D(s_t0,uv);
	vec4 projectimg,texCoordProjNorm;
	texCoordProjNorm = normalize(texCoordProj);	
	projectimg = texture2DProj(s_t0,texCoordProj);
	gl_FragColor = projectimg;
}
#endif
And the image is this
Image
Personally, this seems a weird projection and texture is stretched above spheres. It seems more like that every object preserves its own texture coords. But this approach uses texture2DProj
Meadow Fun!! - my first commercial game, made with FTEQW game engine
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: GL matrix story (stupid but true)

Post by toneddu2000 »

Today I tried a different approach. Instead of using engine builtin matricies, I asked myself: how would it be if I use arbitrary rotation matrix for texture plane projection?
So I learned here how openGL uses matricies for transformation and I created a rotation on X axys matrix

Code: Select all

    mat4 rot_mat_x_axis = mat4(
        vec4(1.0, 0, 0, 0),
        vec4(0,cos(cvar_glsl_project_angle),-sin(cvar_glsl_project_angle),0),
		vec4(0, sin(cvar_glsl_project_angle),cos(cvar_glsl_project_angle),0),
		vec4(0, 0, 0, 1)
    );
which I used as inclination for plane projection.
This is the glsl file

Code: Select all

!!ver 430
!!cvarf glsl_project_angle
#include "sys/defs.h"
varying vec4 ProjTexCoord;
varying vec2 uv;
uniform float cvar_glsl_project_angle;
#ifdef VERTEX_SHADER
void main (void)
{
	mat4 rot_mat_x_axis = mat4(
        vec4(1.0, 0, 0, 0),
        vec4(0,cos(cvar_glsl_project_angle),-sin(cvar_glsl_project_angle),0),
		vec4(0, sin(cvar_glsl_project_angle),cos(cvar_glsl_project_angle),0),
		vec4(0, 0, 0, 1)
    );
	vec4 pos4 = vec4(v_position.xyz, 1.0);
    ProjTexCoord = (m_projection*rot_mat_x_axis*pos4);
	uv.xy = v_texcoord;
	gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0,s_t1;
void main (void)
{
	vec4 texProj = vec4(0.0);
	vec4 texDiff = texture2D(s_t0,uv.xy);
	vec4 finalColor;
    if( ProjTexCoord.z < cvar_glsl_project_angle ){
		 texProj = textureProj( s_t1, ProjTexCoord );
		 finalColor = texProj;
	}
	else{
		finalColor = texDiff;
	}
    gl_FragColor = finalColor;
}
#endif
This is the rendering
Image
you can use cvar_glsl_project_angle to change angle of orientation of the projected plane. As you can see, both spheres have same inclination of projection, cubes seems that have their own. Plus, rotating objects like the cube in the screenshot, rotate the projection with them, so it's again in object coordinates, not world coords. But, really, I cannot understand how to convert it.
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Post Reply