PDA

View Full Version : spaces in the shader



ste3e
10-10-2011, 01:58 PM
I have been implementing a version of Jason L. McKesson's camera model but have run into a problem. Every drawing loop a camera matrix is constructed from quarternions and set to a vertex shader mat4 in the used shader program. The first object to be drawn is a room. It hands off to the shader its location via a mat4 modelMat. The vertex shader is as follows:


#version 330

layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 coord;

uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;

out vec2 coords;
out vec4 position;

void main(){
coords=coord.st * vec2(gl_FrontMaterial.emission.z, gl_FrontMaterial.emission.w);

vec4 tmp=vec4(pos, 1.0);
tmp=tmp * modelMat;
tmp=tmp * viewMat;
gl_Position=tmp * projMat;
}


When the program runs it behaves as expected, the player can run around inside the room, look up/down, strafe. Next I add a pillar to the room. The pillar object has its own model matrix that positions it inside the room. When it is drawn it uses the same shader as the room, the viewMatrix remains unchanged, and the modelMatrix is updated with the pillarís. When I run the program, the pillar is correctly located in the room, it behaves as it should when I turn the camera side to side/up or down, but when I move forward/back/strafe the pillar moves with the camera instead of staying put in the room. Actually, I turned the room off and found that the player moves relative to the pillar but at a much slower rate.

My understanding of the above code is that the vertices are coming into the shader in model space, the model matrix converts them into world space, the camera is a fiction of where the player is in world space and the view matrix makes the fiction happen by spinning the entire world (per vertex) into eye space so that what the fictional camera is viewing actually sits in front of the real camera fixed at 0,0,0 looking 0,0,-1. But if the view matrix is moving every vertex as located in world space, then I donít understand how the pillar can be moving detached from the room given both are initially located accurately in world space, and both use the same view matrix to transform these world space positions into eye space. Is something being updated that I am not aware of?

Alfonse Reinheart
10-10-2011, 03:33 PM
But if the view matrix is moving every vertex as located in world space, then I donít understand how the pillar can be moving detached from the room given both are initially located accurately in world space, and both use the same view matrix to transform these world space positions into eye space.

Therefore, one of the assumptions you are making is incorrect. So either:

1: The camera matrix is not the matrix you expect it to be.

2: The model matrices do not transform to same space. That is, the space you're transforming the room to is not the one you're transforming the pillar to.

3: Some other bug in your code is happening.

We can't really diagnose what's going on from the theory behind what you implemented. What you have is simply a bug in your code: you think you're doing X, but your code is actually doing Y. Without seeing the code, I can't say anything much more than that.

Dan Bartlett
10-10-2011, 05:03 PM
Are you sure you have your matrices in the right form, have you tried multiplying with the matrix in front of the vector?
http://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_Operations#Operators has some useful info.


vec4 tmp=vec4(pos, 1.0);
tmp = modelMat * tmp;
tmp = viewMat * tmp;
gl_Position = projMat * tmp;

ste3e
10-11-2011, 12:45 AM
Sory, I have used the wrong reply button. The code is written in c# (mono) using the Tao framework SDL/OpenGL. I will list the relevant code. The main entry is in the class App which basically initializes SDL, sets up GL and runs the loop. State is a pass around class that holds variables required by most other classes. It contains matrices for projection and view. The matrices are Matrix class objects, a 1 dimension float array of 16 values (row major). They are indexed, using property syntax, ax, ay, az, aw for row 1, bx, bey (by reserved), bz, bw, for row 2 x, y, z, w, etc. Note that I have set the transpose flag on Gl.glUniformMatrix4fv calls to GL_TRUE. App.cs:



private void setOpenGL(){
resize(w, h);

Gl.glEnable(Gl.GL_DOUBLEBUFFER);
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glEnable(Gl.GL_CULL_FACE);

Gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
Gl.glClearDepth(1.0f);

Gl.glDepthMask(Gl.GL_TRUE);
Gl.glDepthFunc(Gl.GL_EQUAL);

Gl.glCullFace(Gl.GL_BACK);
Gl.glFrontFace(Gl.GL_CCW);
}
private void resize(int width, int height){
if(width==0) width=1;
float ratio=((float)height / (float)width);
float frustScale=(float)(1.0 / Math.Tan((degRad * fov) / 2.0));
float ax=frustScale * ratio;
float bey=frustScale;
float cz=(far + near) / (near - far);
float cw=(2.0f * far * near) / (near - far);
float dz=-1.0f;

state.W=width;
state.H=height;
state.Near=near;
state.Far=far;
state.Fov=fov;
state.setProjection(ax, bey, cz, cw, dz);

Gl.glViewport(0, 0, width, height);
Gl.glDepthRange(near, far);
}

private void tick(){
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

ehub.tick();

Gl.glFlush();
Sdl.SDL_GL_SwapBuffers();
}


eHub is an EventHub object that has the Player and the ShaderHub hung off it. The Player code builds quarternions from events passed it by the EventHub, converts these into the view matrix and stores the view matrix in the State object (every loop). Player.cs:



private State state;
private bool hVirgin=true;
private bool pVirgin=true;
private const float _maxPitch=(float)Math.PI*60/180;
public const float _360=(float)Math.PI*2;
private float head=0.0f;
private float pitch=0.0f;
private float vel=0.0f;
private float strf=0.0f;
private bool ford=false;
private bool back=false;
private bool strafel=false;
private bool strafer=false;
private float x, y, z;
private Q q, qHead, qPitch;
private const float walk = 0.01f, strafe = 0.007f;

public Player(State state){
this.state=state;
y=5f;
x=z=0.0f;
qPitch=new Q(0.0f, 0.0f, 0.0f, 1.0f);
qHead=new Q(0.0f, 0.0f, 0.0f, 1.0f);
}

public void setHead(float f){
if(hVirgin){ hVirgin=false; return;}//throw first event away
head+=f;

if(head>_360) head-=_360;
if(head<-_360) head+=_360;
}
public void setPitch(float f){
if(pVirgin){ pVirgin=false; return;}//throw first event away
pitch+=f;

if(pitch>_maxPitch) pitch=_maxPitch;
if(pitch<-_maxPitch) pitch=-_maxPitch;
}
public bool fordP{get{return ford;} set{ford=value;}}
public bool backP{get{return back;} set{back=value;}}
public bool strafelP{get{return strafel;} set{strafel=value;}}
public bool straferP{get{return strafer;} set{strafer=value;}}

public void drawCam(){
if(ford) vel=(float)state.Delay * walk;
if(back) vel=(float)-state.Delay * walk;
if(strafel) strf=(float)-state.Delay * strafe;
if(strafer) strf=(float)state.Delay * strafe;
qPitch.x=(float)Math.Sin(pitch*0.5f);
qPitch.w=(float)Math.Cos(pitch*0.5f);

qHead.y=(float)Math.Sin(head*0.5f);
qHead.w=(float)Math.Cos(head*0.5f);
x+=(1.0f - 2.0f * ( qHead.y * qHead.y + qHead.z * qHead.z ))*strf;//mat[0]
x+=(2.0f * ( qHead.x * qHead.z + qHead.y * qHead.w ))*vel;//mat[8]

z+=(2.0f * (qHead.x * qHead.z - qHead.y * qHead.w))*strf;//mat[2]
z+=(1.0f - 2.0f * ( qHead.x * qHead.x + qHead.y * qHead.y ))*vel;//mat[10]
multQ(ref q, qPitch, qHead);
setMat(q);

strf=0.0f;
vel=0.0f;
}

private void setMat(Q q){
//xbase
state.vax=1.0f - 2.0f * (q.y * q.y + q.z * q.z);
state.vbx=2.0f * (q.x * q.y + q.z * q.w);
state.vcx=2.0f * (q.x * q.z - q.y * q.w);
//ybase
state.vay=2.0f * (q.x * q.y - q.z * q.w);
state.vby=1.0f - 2.0f * (q.x * q.x + q.z * q.z);
state.vcz=2.0f * (q.z * q.y + q.x * q.w);
//zbase
state.vaz=2.0f * (q.x * q.z + q.y * q.w);
state.vbz=2.0f * (q.y * q.z - q.x * q.w);
state.vcz=1.0f - 2.0f * (q.x * q.x + q.y * q.y);
//translation
state.vaw=state.vax * -x + state.vay * -y + state.vaz * z;
state.vbw=state.vbx * -x + state.vby * -y + state.vbz * z;
state.vcw=state.vcx * -x + state.vcy * -y + state.vcz * z;
}

private void multQ(ref Q q, Q a, Q b){
q.w = a.w*b.w - a.x*b.x - a.y*b.y - a.z*b.z;
q.x = a.w*b.x + a.x*b.w + a.y*b.z - a.z*b.y;
q.y = a.w*b.y - a.x*b.z + a.y*b.w + a.z*b.x;
q.z = a.w*b.z + a.x*b.y - a.y*b.x + a.z*b.w;
}

public void draw(){
//called from the EventHub per loop
drawCam();
}


The ShaderHub, hung off the EventHub along with the Player, will have shaders for handling deferred rendering/shading, and shaders for handling drawing to the g buffers. At this iteration it sets up the shader for drawing to gl_Fragment, and then initializes the loading of the room from an xml file. The room is a Quad object constructed from xml data. The xml file contains a list of further objects inside the Quad... pillars eg. Rather than using a scene graph I am playing with the idea of using a linked list of Quads... the current Quad is the one the Player is in, and the head of a linked list of quads the player can see. The projection matrix is attached to shader in the constructor. The view matrix is attached in the draw method once the shader is used. ShaderHub.cs



public ShaderHub(State state){
this.state=state;
qVault=new QuadVault(state);
tVault=new TexVault(state);
mVault=new ModelVault();

shad=Bin._shad;
if(shad<1) Console.WriteLine("Error: GL did not assign shader id");
vshad=Bin.createShader(state.Path + "Game\\assets\\shaders\\main.vert", Bin._vertShad);//0 flags vert shader
if(vshad<1){Console.WriteLine("GL failed to give vert shader id");}
fshad=Bin.createShader(state.Path + "Game\\assets\\shaders\\main.frag", Bin._fragShad);
if(fshad<1){Console.WriteLine("GL failed to give frag shader id");}
bool flag=Bin.setShader(shad, vshad, fshad);
if(!flag){Console.WriteLine("Error linking shader.");}

modelLoc=Gl.glGetUniformLocation(shad, "modelMat");
if(modelLoc == -1){Console.WriteLine("model matrix location not got");}
state.ModelLoc=modelLoc;
projLoc=Gl.glGetUniformLocation(shad, "projMat");
if(projLoc == -1){Console.WriteLine("matrix projMat location not got");}
viewLoc=Gl.glGetUniformLocation(shad, "viewMat");
if(viewLoc == -1){Console.WriteLine("matrix viewMat location not got");}

Gl.glUseProgram(shad);
Gl.glUniformMatrix4fv(projLoc, 1, Gl.GL_TRUE, state.ProjMat);
Gl.glUseProgram(0);

tileLoc=Gl.glGetUniformLocation(Bin._shad, "colMap");
if(tileLoc==-1) Console.WriteLine("Error: sampler tileLoc not got");
state.TileLoc=this.tileLoc;

//tVault is texture vault. mVault is modelVault. qVault is quad vault
current=QuadReader.readQuad(state, tVault);
current.setModels(tVault, mVault);
qVault.addQuad(current);
}

public void draw(){
Gl.glUseProgram(shad);
Gl.glUniformMatrix4fv(viewLoc, 1, Gl.GL_TRUE, state.ViewMat);

current.draw();//draw the current Quad

Gl.glUseProgram(0);
}


The Quad object draws itself as below. North, east, etc refer to walls; whether the room has a wall. modelMat is the matrix containing that defines the world space orientation for this quad. In this instance it is an identity matrix so I have not shown it. The Mat suffix is used to get the float array of the matrix. Quad.cs:



public void setModels(TexVault t, ModelVault m){
for(int i=0; i<smodels.Length; i++){
ModelReader.readModel(ref smodels[i], t, m, state);
smodels[i].init();
}
}

public void draw(){
counter=0;
Gl.glActiveTexture(Gl.GL_TEXTURE0);

Gl.glUniformMatrix4fv(state.ModelLoc, 1, Gl.GL_TRUE, modelMat.Mat);

Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);

Gl.glEnableVertexAttribArray(0);
Gl.glEnableVertexAttribArray(1);
Gl.glEnableVertexAttribArray(2);
Gl.glVertexAttribPointer(0, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, voff);
Gl.glVertexAttribPointer(1, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, noff);
Gl.glVertexAttribPointer(2, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, coff);

//the material's emission vector ports tiling info to alter coords in the vert shader
coordFlags[2]=fts; coordFlags[3]=ftt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, ftid);
Gl.glUniform1i(state.TileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
if(north){
counter+=4;
coordFlags[2]=nts; coordFlags[3]=ntt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, ntid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(east){
counter+=4;
coordFlags[2]=ets; coordFlags[3]=ett;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, etid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(south){
counter+=4;
coordFlags[2]=sts; coordFlags[3]=stt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, etid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
if(west){
counter+=4;
coordFlags[2]=wts; coordFlags[3]=wtt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, wtid);
Gl.glUniform1i(tileLoc, 0);
Gl.glDrawArrays(Gl.GL_QUADS, counter, 4);
}
Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);

smodels[0].draw();
}


smodels is an array of StaticModel objects (pillars) that was loaded when when the Quad was loaded. If this line is commented out everything works as expected. The model matrix for the pillar positions the pillar in world space independent from the quad. StaticModel.cs:




public void init(){
//the offsets are read from the relevant xml file and inserted into an identity matrix
modelMat.aw+=xoff;
modelMat.bw+=yoff;
modelMat.cw+=zoff;
}

public void draw(){
Gl.glActiveTexture(Gl.GL_TEXTURE0);
Gl.glUniform1i(state.TileLoc, 0);

Gl.glUniformMatrix4fv(state.ModelLoc, 1, Gl.GL_TRUE, modelMat.Mat);

Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, vbo);

Gl.glEnableVertexAttribArray(0);
Gl.glEnableVertexAttribArray(1);
Gl.glEnableVertexAttribArray(2);
Gl.glVertexAttribPointer(0, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, voff);
Gl.glVertexAttribPointer(1, 3, Gl.GL_FLOAT, Gl.GL_FALSE, 0, noff);
Gl.glVertexAttribPointer(2, 2, Gl.GL_FLOAT, Gl.GL_FALSE, 0, coff);

coordFlags[2]=ts; coordFlags[3]=tt;
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_EMISSION, coordFlags);
Gl.glBindTexture(Gl.GL_TEXTURE_2D, tid);
Gl.glDrawArrays(Gl.GL_QUADS, 0, vcount);

Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, 0);
}


For some reason the pillar moves with the player. The State class holds the view and the projection matrices. Probably silly of me but I decided to place a separate float array in the state class to handle the view matrix because it gets updated every loop via from the Player class. State.cs:



private string path, level="Game\\assets\\levels\\", quadKey="hellsKitchen\\subway.xml";
private long delay;
private Matrix projMat;
private int w, h;
private float near, far, fov;
private int tileLoc, modelLoc, viewLoc;

private float[] view=new float[]{ 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};

public State(){
path=Environment.CurrentDirectory;
string[] dirs=path.Split('\\');
path="";
for(int i=0;i<(dirs.Length-3);i++){
path+=dirs[i];
path+="\\";
}
projMat=new Matrix();
}

public void setProjection(float ax, float bey, float cz, float cw, float dz){
projMat.ax=ax;
projMat.bey=bey;
projMat.cz=cz;
projMat.cw=cw;
projMat.dz=dz;
}

//properties used to set the view matrix
public float vax{get{return view[0];}set{view[0]=value;}}
public float vay{get{return view[1];}set{view[1]=value;}}
public float vaz{get{return view[2];}set{view[2]=value;}}
public float vaw{get{return view[3];}set{view[3]=value;}}
public float vbx{get{return view[4];}set{view[4]=value;}}
public float vby{get{return view[5];}set{view[5]=value;}}
public float vbz{get{return view[6];}set{view[6]=value;}}
public float vbw{get{return view[7];}set{view[7]=value;}}
public float vcx{get{return view[8];}set{view[8]=value;}}
public float vcy{get{return view[9];}set{view[9]=value;}}
public float vcz{get{return view[10];}set{view[10]=value;}}
public float vcw{get{return view[11];}set{view[11]=value;}}
public float vdx{get{return view[12];}set{view[12]=value;}}
public float vdy{get{return view[13];}set{view[13]=value;}}
public float vdz{get{return view[14];}set{view[14]=value;}}
public float vdw{get{return view[15];}set{view[15]=value;}}
public float[] ViewMat{get{return view;}set{view=value;}}
public int ModelLoc{get{return modelLoc;}set{modelLoc=value;}}
public int ViewLoc{get{return viewLoc;}set{viewLoc=value;}}
public int TileLoc{get{return tileLoc;}set{tileLoc=value;}}


The Matrix class is pretty simple. Matrix.cs:



using System;

namespace Game
{
public class Matrix{
private float[] mat=new float[]{ 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};

public Matrix(){}

public float[] Mat{get{return mat;}set{mat=value;}}
public float ax{get{return mat[0];}set{mat[0]=value;}}
public float ay{get{return mat[1];}set{mat[1]=value;}}
public float az{get{return mat[2];}set{mat[2]=value;}}
public float aw{get{return mat[3];}set{mat[3]=value;}}
public float bx{get{return mat[4];}set{mat[4]=value;}}
public float bey{get{return mat[5];}set{mat[5]=value;}}
public float bz{get{return mat[6];}set{mat[6]=value;}}
public float bw{get{return mat[7];}set{mat[7]=value;}}
public float cx{get{return mat[8];}set{mat[8]=value;}}
public float cy{get{return mat[9];}set{mat[9]=value;}}
public float cz{get{return mat[10];}set{mat[10]=value;}}
public float cw{get{return mat[11];}set{mat[11]=value;}}
public float dx{get{return mat[12];}set{mat[12]=value;}}
public float dy{get{return mat[13];}set{mat[13]=value;}}
public float dz{get{return mat[14];}set{mat[14]=value;}}
public float dw{get{return mat[15];}set{mat[15]=value;}}
}
}


I have altered the vert shader as Dan pointed out. Still the same problem however. main.vert



#version 330

layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 norm;
layout(location = 2) in vec2 coord;

uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;

out vec2 coords;
out vec4 position;

void main(){
coords=coord.st * vec2(gl_FrontMaterial.emission.z, gl_FrontMaterial.emission.w);

vec4 worldPos=modelMat * vec4(pos, 1.0);
vec4 eyePos=viewMat * worldPos;
gl_Position=projMat * eyePos;
}

ste3e
10-11-2011, 05:35 PM
I managed to track the problem down, but for the life of me I don't understand it. I don't believe it is a GL problem but something weird with c# mono. If I use:


System.Convert.ToSingle(tmp[1])

to pass a float variable read from an xml file to initialize the x position of the model's matrix the model will follow the camera. If, on the other hand, I pass


20f to initialize the x position, the model stays where it ought, and behaves as it ought.



public void setModels(TexVault t, ModelVault m){
string[] tmp=staticModels[0].Split(new char[]{' '});
float x= System.Convert.ToSingle(tmp[1]);
//smodels=new StaticModel(staticModels[0], x);//follows the camera
smodels=new StaticModel(staticModels[0], 20f);//behaves as it ought
}


Too weird. Don't spose anyone has an inkling as to what is going on? Furthermore, I have printed out the value of x, when passes from the xml, for every loop, and it remains constant.

ste3e
10-11-2011, 06:11 PM
Scratch the last. I just found that both behave the same if handed the same value. Large values cause the object to follow the camera, small values see the object sit where it is placed.

Problem solved. I had altered the values in the room's xml file but not in the pillar's positioning or size. For some reason, which is bound to confound me later, the pillar even though it was positioned well behind the rooms walls was not being depth culled.