Sign in to follow this  

Geometry shader, point sprites to spheres

This topic is 2545 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'm working on transforming a set of point sprites to sphere using a geometry shader (i found that it could be a good idea).
RIght now, the vertex and fragment shader are working, they make the point sprites look like sphere but in 2D (the texture of the sphere is always facing you). When i'm trying to use the geometry shader below, nothing is rendered. HELP!!! lol!
Here the code :

// vertex shader
uniform float pointRadius; // point size in world space
uniform float pointScale; // scale to calculate size in pixels
uniform vec4 eyePos;
void main()
{
vec4 wpos = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * wpos;

// calculate window-space point size
vec4 eyeSpacePos = gl_ModelViewMatrix * wpos;
float dist = length(eyeSpacePos.xyz);
gl_PointSize = pointRadius * (pointScale / dist);

gl_TexCoord[0] = gl_MultiTexCoord0; // sprite texcoord
gl_TexCoord[1] = eyeSpacePos;
gl_FrontColor = gl_Color;
}



// pixel shader for rendering points as shaded spheres
uniform float pointRadius;
uniform vec3 lightDir = vec3(0.577, 0.577, 0.577);
void main()
{
// calculate eye-space sphere normal from texture coordinates
vec3 N;
N.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);
float r2 = dot(N.xy, N.xy);
if (r2 > 1.0) discard; // kill pixels outside circle
N.z = sqrt(1.0-r2);

// calculate depth
vec4 eyeSpacePos = vec4(gl_TexCoord[1].xyz + N*pointRadius, 1.0); // position of this pixel on sphere in eye space
vec4 clipSpacePos = gl_ProjectionMatrix * eyeSpacePos;
gl_FragDepth = (clipSpacePos.z / clipSpacePos.w)*0.5+0.5;

float diffuse = max(0.0, dot(N, lightDir));

gl_FragColor = diffuse*gl_Color;
}



// geometry shader
//#version 120
//#extension GL_EXT_geometry_shader4 : enable
const float radius = 0.5;
varying out vec2 coord;
void main()
{
for (int i = 0 ; i < gl_VerticesIn ; ++i )
{
gl_FrontColor = gl_FrontColorIn[i];
gl_Position = gl_PositionIn[i];
EmitVertex ( );
}
gl_FrontColor = gl_FrontColorIn[0];

coord = vec2( -1,-1 );
gl_Position = (gl_PositionIn[0] + gl_ProjectionMatrix * vec4(-radius,-radius,0,0) );
EmitVertex();
coord = vec2( -1,1 );
gl_Position = (gl_PositionIn[0] + gl_ProjectionMatrix * vec4(-radius,radius, 0,0) );
EmitVertex();
coord = vec2( 1,-1 );
gl_Position = (gl_PositionIn[0] + gl_ProjectionMatrix * vec4( radius,-radius, 0,0) );
EmitVertex();
coord = vec2( 1,1 );
gl_Position = (gl_PositionIn[0] + gl_ProjectionMatrix * vec4( radius,radius, 0,0) );
EmitVertex();
EndPrimitive();
}

Share this post


Link to post
Share on other sites
This stuff is way beyond me, but my first thought is that do you really need a geometry shader for this? Wouldn't a pixel shader with altering the depth values and with some nice spherical texture coordinate lookup thingy suffice?

Share this post


Link to post
Share on other sites
Quote:
Original post by szecs
This stuff is way beyond me, but my first thought is that do you really need a geometry shader for this? Wouldn't a pixel shader with altering the depth values and with some nice spherical texture coordinate lookup thingy suffice?


Well, when i started, i wanted to use the pixel shader to do this, but i was unable to get a good result. I also didnt find anyone that could help me do this. If you have an idea, or example of code using this technique with pixel shader, let me know.

Share this post


Link to post
Share on other sites
Quote:
Original post by Danny02
instead of calculating the normal and depth, what about using a lookup texture

render a texture of a sphere in rgb the normal and in the alpha channel the depth


But it won't help the fact that i'm unable to rotate around the sphere. Since i will be using light, i want to be able to rotate around the sphere and see the light changing.

Share this post


Link to post
Share on other sites
when u calculate the hole lighting in view space it shouldn't be a problem cause the normals of the sphere don't change in view space

multiply the lightdir with the viewmatrix and u will get ur desired result

Share this post


Link to post
Share on other sites
Quote:
Original post by Danny02
when u calculate the hole lighting in view space it shouldn't be a problem cause the normals of the sphere don't change in view space

multiply the lightdir with the viewmatrix and u will get ur desired result


You mean i should change the line
uniform vec3 lightDir = vec3(0.577, 0.577, 0.577);

to

varying vec3 lightDir = vec3(0.577, 0.577, 0.577);
and that i should use

float diffuse = max(0.0, dot(N, gl_ModelViewMatrix * lightDir));

or something like that? Tryed it and it didnt work. Here a picture of my actual project, with the sphere always facing you.

image

Share this post


Link to post
Share on other sites
directional light source:

multiply the light direction with the view(camera) matrix only, not the modelview, in the vertex shader.
Use the resulting vector in the dot product with the normal vector in the fragment shader

point light source:
you have the lightposition already in worldspace so u also have only to multiply it with the view matrix. Then u have to calculate the the lightdirection for every pixel either in the vertex or the fragment shader(don't know atm if the vertex shader approch has the same result as if it is calculated in the fragment shader)


I think your problem is that the old deprecated openGL functionality don't offer u direct access to only the view matrix. So if you want to do this technique u have to create and upload the matrix yourself as a uniform.

Share this post


Link to post
Share on other sites
A few comments / suggestions:

You're outputting a varying "coord" which isn't picked up at all by the fragment shader.

The calculation of the last positions in the geometry shader is bizarre - what is this meant to do? Also, I'm not sure how many vertices your primitive is meant to have, have you missed an EndPrimitive() call after the loop?

For starters I suggest getting a simple pass-through geometry shader to work before you try to do anything with it.

I've only played about with #version 400 geometry shaders, but there's a max_vertices layout specifier that needs setting, I'm not sure if there's an equivalent in pre-400?

You might need to pass through texture coordinates as well as front color and position?

Share this post


Link to post
Share on other sites
Quote:
Original post by Danny02
directional light source:

multiply the light direction with the view(camera) matrix only, not the modelview, in the vertex shader.
Use the resulting vector in the dot product with the normal vector in the fragment shader

point light source:
you have the lightposition already in worldspace so u also have only to multiply it with the view matrix. Then u have to calculate the the lightdirection for every pixel either in the vertex or the fragment shader(don't know atm if the vertex shader approch has the same result as if it is calculated in the fragment shader)


I think your problem is that the old deprecated openGL functionality don't offer u direct access to only the view matrix. So if you want to do this technique u have to create and upload the matrix yourself as a uniform.


Directional light source :

Should it be something like that?

varying vec3 lightDir;
void main()
{
vec4 wpos = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * wpos;
//lightDir = gl_Position * vec3(0.577, 0.577, 0.577);
// calculate window-space point size
vec4 eyeSpacePos = gl_ModelViewMatrix * wpos;
lightDir = eyeSpacePos.xyz * vec3(0.1, 0.1, 0.1);

Share this post


Link to post
Share on other sites

vertex-shader:
uniform vec3 lightDir;
uniform mat3 mat_view;

varying vec3 lDir;
varying vec2 texcoord;

void main()
{
vec4 wpos = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * wpos;

texcoord = gl_Texcoord0; //?
vec4 lDir = mat_view * lightDir;
}

fragment-shader:

varying vec3 lDir;
varying vec2 texcoord;
uniform sampler2D normal_sphere;
void main()
{
vec3 N = texture2D(normal_sphere,texcoord).xyz * 2. - 1.;
N = normalize(N);
vec3 L = normalize(lDir);

gl_FragColor = vec4(max(0., dot(L,N)));
}




or


vertex-shader:

varying vec2 texcoord;

void main()
{
vec4 wpos = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * wpos;
texcoord = gl_Texcoord0; //?
}

fragment-shader:

uniform vec3 lightDir; // multiply with the view matrix in your render Code
varying vec2 texcoord;
uniform sampler2D normal_sphere;
void main()
{
vec3 N = texture2D(normal_sphere,texcoord).xyz * 2. - 1.;
N = normalize(N);

gl_FragColor = vec4(max(0., dot(lightDir,N)));
}


Share this post


Link to post
Share on other sites
Ok, i tried your code, and realized i need to declare the uniform in my main .cpp file.
uniform mat3 mat_view;
and
uniform sampler2D normal_sphere;

i know i have to call them using the glUniform1f function.
glUniform1f( glGetUniformLocation(m_shaderProgram, "mat_view"), ??????);
glUniform1f( glGetUniformLocation(m_shaderProgram, "normal_sphere"), ??????);

I think that will fix it, because right now, the points are not showing as spheres.

Thanks again!

Share this post


Link to post
Share on other sites
in my code i used a texture for the normal lookup u can ofcourse use your code which calculates the Normal for the sphere. But a Texture lookup will be much faster.

for the view matrix, either u calculate it your self with a math libary. or u can use the old openGL functionality(which also won't be the fastest way).

after u set up your camera in the beginning of each render/display call (normally in the gl_ModelView matrix) u can retrive that matrix from openGL with a function (don't know atm you have to look it up) and then when you got this float array u have to upload it to the shader as a uniform(not the 1f one u posted, there should be one extra for matrices)

Share this post


Link to post
Share on other sites
Quote:
Original post by Danny02
in my code i used a texture for the normal lookup u can ofcourse use your code which calculates the Normal for the sphere. But a Texture lookup will be much faster.

for the view matrix, either u calculate it your self with a math libary. or u can use the old openGL functionality(which also won't be the fastest way).

after u set up your camera in the beginning of each render/display call (normally in the gl_ModelView matrix) u can retrive that matrix from openGL with a function (don't know atm you have to look it up) and then when you got this float array u have to upload it to the shader as a uniform(not the 1f one u posted, there should be one extra for matrices)


I followed your tips and tried to get a viewMatrix. Everything is compiling without any errors. I'm just not getting sphere, which mean something is wrong somewhere. I'll post most of the code used for transforming the point sprite in sphere. Here the code from my camera file for the view matrix :

//in camera.h
const Matrix4 &getViewMatrix() const;
Matrix4 m_viewMatrix;

//in camera.cpp

void Camera::lookAt(const Vector3 &eye, const Vector3 &target, const Vector3 &up)
{
m_eye = eye;
m_target = target;

m_zAxis = eye - target;
m_zAxis.normalize();

m_viewDir = -m_zAxis;

m_xAxis = Vector3::cross(up, m_zAxis);
m_xAxis.normalize();

m_yAxis = Vector3::cross(m_zAxis, m_xAxis);
m_yAxis.normalize();

m_viewMatrix[0][0] = m_xAxis.x;
m_viewMatrix[1][0] = m_xAxis.y;
m_viewMatrix[2][0] = m_xAxis.z;
m_viewMatrix[3][0] = -Vector3::dot(m_xAxis, eye);

m_viewMatrix[0][1] = m_yAxis.x;
m_viewMatrix[1][1] = m_yAxis.y;
m_viewMatrix[2][1] = m_yAxis.z;
m_viewMatrix[3][1] = -Vector3::dot(m_yAxis, eye);

m_viewMatrix[0][2] = m_zAxis.x;
m_viewMatrix[1][2] = m_zAxis.y;
m_viewMatrix[2][2] = m_zAxis.z;
m_viewMatrix[3][2] = -Vector3::dot(m_zAxis, eye);




Now the code for the vertex shader (i used some of your code example)

uniform float pointRadius; // point size in world space
uniform float pointScale; // scale to calculate size in pixels
uniform vec3 lightDir = vec3(0.577, 0.577, 0.577);
uniform mat3 mat_view;
varying vec3 lDir;
void main()
{
vec4 wpos = vec4(gl_Vertex.xyz, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * wpos;

vec4 eyeSpacePos = gl_ModelViewMatrix * wpos;

float dist = length(eyeSpacePos.xyz);
gl_PointSize = pointRadius * (pointScale / dist);

vec4 lDir = mat_view * lightDir;

gl_TexCoord[1] = eyeSpacePos;
gl_FrontColor = gl_Color;
}




The pixel shader

uniform float pointRadius;
varying vec3 lDir;
void main()
{
// calculate eye-space sphere normal from texture coordinates
vec3 N;
N.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);
float r2 = dot(N.xy, N.xy);
if (r2 > 1.0) discard; // kill pixels outside circle
N.z = sqrt(1.0-r2);

// calculate depth
vec4 eyeSpacePos = vec4(gl_TexCoord[1].xyz + N*pointRadius, 1.0); // position of this pixel on sphere in eye space
vec4 clipSpacePos = gl_ProjectionMatrix * eyeSpacePos;
gl_FragDepth = (clipSpacePos.z / clipSpacePos.w)*0.5+0.5;

vec3 L = normalize(lDir);
gl_FragColor = vec4(max(0.0, dot(L,N)));
}




and the code in my main file for the uniform :

glUniform1f( glGetUniformLocation(m_shaderProgram, "pointScale"), g_windowHeight / tan(60.0f*0.35f*M_PI/180.0f) );
glUniform1f( glGetUniformLocation(m_shaderProgram, "pointRadius"), 0.3f );
glUniformMatrix4fv( glGetUniformLocation(m_shaderProgram, "mat_view"), 3, 0, &g_camera.getViewMatrix()[0][0]);


Share this post


Link to post
Share on other sites
ok lets see:

- i just assume that your look at function works
- point size stuff shouldn't matter, because you already got spheres to display with that code

viewmatrix:
you have a 3x3 matrix in your shader but are trying to send a 4x4 matrix. 2nd the way you are sending the matrix is wrong.

first we need a 3x3 matrix, so just trim your viewmatrix to one(or create only a 3x3 array and leaf the calculations for the 3 row/column of your lookat function)

now your uniform set function should look like this:
glUniformMatrix3fv( location , 1, false, g_camera.getViewMatrix());


fragment depth:
if you would use a lookup texture u could store all the depth in the alpha channel.
I would recommend to render the normal and depth once to a texture then use that texture for further use in your app(either save the texture to a file or create it at startup)


ps: I don't know if it is so wise to use a 2D array for your matrices, my C isn't that good but the memory layout of the array could be compiler or OS depended and would so screw up your app.

Share this post


Link to post
Share on other sites
Quote:
Original post by Danny02
ok lets see:

- i just assume that your look at function works
- point size stuff shouldn't matter, because you already got spheres to display with that code

viewmatrix:
you have a 3x3 matrix in your shader but are trying to send a 4x4 matrix. 2nd the way you are sending the matrix is wrong.

first we need a 3x3 matrix, so just trim your viewmatrix to one(or create only a 3x3 array and leaf the calculations for the 3 row/column of your lookat function)

now your uniform set function should look like this:
glUniformMatrix3fv( location , 1, false, g_camera.getViewMatrix());


fragment depth:
if you would use a lookup texture u could store all the depth in the alpha channel.
I would recommend to render the normal and depth once to a texture then use that texture for further use in your app(either save the texture to a file or create it at startup)


ps: I don't know if it is so wise to use a 2D array for your matrices, my C isn't that good but the memory layout of the array could be compiler or OS depended and would so screw up your app.


I found a set of texture for a sphere the other day. I wish i knew how to use them!

depth
sphere




Share this post


Link to post
Share on other sites

This topic is 2545 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this