GL3: Render Particles, simplify GL3_ShutdownShaders()

* The particles look more fuzzy than in old renderer - I think it looks
  better this way ;)
* Not sure I keep the way they're rendered - instead of calculating and
  passing the distance in GL3_ShutdownShaders() I could set the player
  (camera) origin in a UBO and calculate distance (and based on that
  the size) in the vertex shader. I could also pass the basic point size
  via UBO, it's the same for all particles..
* Deleting shader programs is a lot shorter now and using a loop and
  the fact that consecutive fields of the same type in a struct have
  the same memory layout as an array of that type.
  Now I can just add a gl3ShaderInfo_t to gl3state, set it up in
  GL3_InitShaders() and don't have to add anything to
  GL3_ShutdownShaders() (this is good, I forgot that all the time and
  didn't notice, as it doesn't cause visible errors)
This commit is contained in:
Daniel Gibson 2017-02-26 00:47:27 +01:00
parent f3e77e1123
commit 247a81c69b
4 changed files with 200 additions and 33 deletions

View file

@ -86,6 +86,7 @@ cvar_t *gl_anisotropic;
cvar_t *gl_texturemode;
cvar_t *gl_drawbuffer;
cvar_t *gl_clear;
cvar_t *gl_particle_size;
cvar_t *gl_lefthand;
cvar_t *gl_farsee;
@ -195,6 +196,7 @@ GL3_Register(void)
gl_mode = ri.Cvar_Get("gl_mode", "4", CVAR_ARCHIVE);
gl_customwidth = ri.Cvar_Get("gl_customwidth", "1024", CVAR_ARCHIVE);
gl_customheight = ri.Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE);
gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE);
gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0);
gl_drawentities = ri.Cvar_Get("gl_drawentities", "1", 0);
@ -247,7 +249,7 @@ GL3_Register(void)
gl_particle_min_size = ri.Cvar_Get("gl_particle_min_size", "2", CVAR_ARCHIVE);
gl_particle_max_size = ri.Cvar_Get("gl_particle_max_size", "40", CVAR_ARCHIVE);
gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE);
//gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE);
gl_particle_att_a = ri.Cvar_Get("gl_particle_att_a", "0.01", CVAR_ARCHIVE);
gl_particle_att_b = ri.Cvar_Get("gl_particle_att_b", "0.0", CVAR_ARCHIVE);
gl_particle_att_c = ri.Cvar_Get("gl_particle_att_c", "0.01", CVAR_ARCHIVE);
@ -799,6 +801,120 @@ GL3_DrawNullModel(void)
#endif // 0
}
#if 0
static void
GL3_PolyBlend(void)
{
if (!gl_polyblend->value)
{
return;
}
if (!v_blend[3])
{
return;
}
glDisable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glLoadIdentity();
glRotatef(-90, 1, 0, 0); /* put Z going up */
glRotatef(90, 0, 0, 1); /* put Z going up */
glColor4f( v_blend[0], v_blend[1], v_blend[2], v_blend[3] );
GLfloat vtx[] = {
10, 100, 100,
10, -100, 100,
10, -100, -100,
10, 100, -100
};
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, vtx );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glEnable(GL_ALPHA_TEST);
glColor4f(1, 1, 1, 1);
}
#endif // 0
static void
GL3_DrawParticles(void)
{
// TODO: stereo
//qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation);
//qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation);
//if (gl_config.pointparameters && !(stereo_split_tb || stereo_split_lr))
{
int i;
int numParticles = gl3_newrefdef.num_particles;
unsigned char color[4];
const particle_t *p;
// assume the size looks good with window height 600px and scale according to real resolution
float pointSize = gl_particle_size->value * (float)gl3_newrefdef.height/600.0f;
typedef struct part_vtx {
GLfloat pos[3];
GLfloat size;
GLfloat dist;
GLfloat color[4];
} part_vtx;
part_vtx buf[numParticles];
// FIXME: viewOrg could be in UBO
vec3_t viewOrg;
VectorCopy(gl3_newrefdef.vieworg, viewOrg);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glEnable(GL_PROGRAM_POINT_SIZE);
GL3_UseProgram(gl3state.siParticle.shaderProgram);
for ( i = 0, p = gl3_newrefdef.particles; i < numParticles; i++, p++ )
{
//*(int *) color = d_8to24table [ p->color & 0xFF ];
memcpy(color, &d_8to24table[ p->color & 0xFF ], 4);
part_vtx* cur = &buf[i];
vec3_t offset; // between viewOrg and particle position
VectorSubtract(viewOrg, p->origin, offset);
VectorCopy(p->origin, cur->pos);
cur->size = pointSize;
cur->dist = VectorLength(offset);
for(int j=0; j<3; ++j) cur->color[j] = color[j]/255.0f;
cur->color[3] = p->alpha;
}
assert(sizeof(part_vtx)==9*sizeof(float));
GL3_BindVAO(gl3state.vaoParticle);
GL3_BindVBO(gl3state.vboParticle);
glBufferData(GL_ARRAY_BUFFER, sizeof(part_vtx)*numParticles, buf, GL_STREAM_DRAW);
glDrawArrays(GL_POINTS, 0, numParticles);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glDisable(GL_PROGRAM_POINT_SIZE);
}
}
static void
GL3_DrawEntitiesOnList(void)
{
@ -853,7 +969,6 @@ GL3_DrawEntitiesOnList(void)
}
}
#if 1
/* draw transparent entities
we could sort these if it ever
becomes a problem... */
@ -901,7 +1016,6 @@ GL3_DrawEntitiesOnList(void)
}
glDepthMask(1); /* back to writing */
#endif // 0
}
static int
@ -1369,9 +1483,10 @@ GL3_RenderView(refdef_t *fd)
GL3_DrawEntitiesOnList();
#if 0 // TODO !!
GL3_RenderDlights();
R_DrawParticles();
#endif // 0
GL3_DrawParticles();
GL3_DrawAlphaSurfaces();
#if 0
R_Flash();

View file

@ -412,6 +412,51 @@ static const char* fragmentSrcAliasColor = MULTILINE_STRING(
}
);
static const char* vertexSrcParticles = MULTILINE_STRING(
// it gets attributes and uniforms from vertexCommon3D
out vec4 passColor;
void main()
{
passColor = vertColor;
gl_Position = transProj * transModelView * vec4(position, 1.0);
// abusing texCoord for pointSize, pointDist for particles
float pointDist = texCoord.y*0.1; // with factor 0.1 it looks good.
// 1.4 to make them a bit bigger, they look smaller due to fading (see fragment shader)
gl_PointSize = 1.4*texCoord.x/pointDist;
}
);
static const char* fragmentSrcParticles = MULTILINE_STRING(
// it gets attributes and uniforms from fragmentCommon3D
in vec4 passColor;
void main()
{
vec2 offsetFromCenter = 2.0*(gl_PointCoord - vec2(0.5, 0.5)); // normalize so offset is between 0 and 1 instead 0 and 0.5
float distSquared = dot(offsetFromCenter, offsetFromCenter);
if(distSquared > 1.0) // this makes sure the particle is round
discard;
vec4 texel = passColor;
// apply gamma correction and intensity
//texel.rgb *= intensity; TODO: intensity? Probably not?
outColor.rgb = pow(texel.rgb, vec3(gamma));
// I want the particles to fade out towards the edge, the following seems to look nice
texel.a *= min(1.0, 1.2*(1.0 - distSquared));
outColor.a = texel.a; // I think alpha shouldn't be modified by gamma and intensity
}
);
#undef MULTILINE_STRING
@ -676,7 +721,11 @@ qboolean GL3_InitShaders(void)
R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering flat-colored models!\n");
return false;
}
if(!initShader3D(&gl3state.siParticle, vertexSrcParticles, fragmentSrcParticles))
{
R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering particles!\n");
return false;
}
gl3state.currentShaderProgram = 0;
@ -685,34 +734,15 @@ qboolean GL3_InitShaders(void)
void GL3_ShutdownShaders(void)
{
if(gl3state.si2D.shaderProgram != 0)
glDeleteProgram(gl3state.si2D.shaderProgram);
memset(&gl3state.si2D, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si2Dcolor.shaderProgram != 0)
glDeleteProgram(gl3state.si2Dcolor.shaderProgram);
memset(&gl3state.si2Dcolor, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si3D.shaderProgram != 0)
glDeleteProgram(gl3state.si3D.shaderProgram);
memset(&gl3state.si3D, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si3Dflow.shaderProgram != 0)
glDeleteProgram(gl3state.si3Dflow.shaderProgram);
memset(&gl3state.si3Dflow, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si3Dturb.shaderProgram != 0)
glDeleteProgram(gl3state.si3Dturb.shaderProgram);
memset(&gl3state.si3Dturb, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si3Dalias.shaderProgram != 0)
glDeleteProgram(gl3state.si3Dalias.shaderProgram);
memset(&gl3state.si3Dalias, 0, sizeof(gl3ShaderInfo_t));
if(gl3state.si3DaliasColor.shaderProgram != 0)
glDeleteProgram(gl3state.si3DaliasColor.shaderProgram);
memset(&gl3state.si3DaliasColor, 0, sizeof(gl3ShaderInfo_t));
const gl3ShaderInfo_t siZero = {0};
for(gl3ShaderInfo_t* si = &gl3state.si2D; si <= &gl3state.siParticle; ++si)
{
if(si->shaderProgram != 0) glDeleteProgram(si->shaderProgram);
*si = siZero;
}
// let's (ab)use the fact that all 3 UBO handles are consecutive fields
// of the gl3state struct
glDeleteBuffers(3, &gl3state.uniCommonUBO);
gl3state.uniCommonUBO = gl3state.uni2DUBO = gl3state.uni3DUBO = 0;
}

View file

@ -76,6 +76,22 @@ void GL3_SurfInit(void)
glEnableVertexAttribArray(GL3_ATTRIB_COLOR);
qglVertexAttribPointer(GL3_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
glGenVertexArrays(1, &gl3state.vaoParticle);
GL3_BindVAO(gl3state.vaoParticle);
glGenBuffers(1, &gl3state.vboParticle);
GL3_BindVBO(gl3state.vboParticle);
glEnableVertexAttribArray(GL3_ATTRIB_POSITION);
qglVertexAttribPointer(GL3_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0);
glEnableVertexAttribArray(GL3_ATTRIB_TEXCOORD); // it's abused for (point_size, distance) here..
qglVertexAttribPointer(GL3_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat));
glEnableVertexAttribArray(GL3_ATTRIB_COLOR);
qglVertexAttribPointer(GL3_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat));
}
void GL3_SurfShutdown(void)

View file

@ -162,6 +162,8 @@ typedef struct
GLuint currentVAO;
GLuint currentVBO;
GLuint currentShaderProgram;
// NOTE: make sure si2D is always the first shaderInfo (or adapt GL3_ShutdownShaders())
gl3ShaderInfo_t si2D; // shader for rendering 2D with textures
gl3ShaderInfo_t si2Dcolor; // shader for rendering 2D with flat colors
gl3ShaderInfo_t si3D;
@ -170,8 +172,12 @@ typedef struct
gl3ShaderInfo_t si3Dalias; // for models
gl3ShaderInfo_t si3DaliasColor; // for models w/ flat colors
// NOTE: make sure siParticle is always the last shaderInfo (or adapt GL3_ShutdownShaders())
gl3ShaderInfo_t siParticle; // for particles. surprising, right?
GLuint vao3D, vbo3D; // for brushes etc, using 7 floats as vertex input (x,y,z, s,t, lms,lmt)
GLuint vaoAlias, vboAlias; // for models, using 9 floats as (x,y,z, s,t, r,g,b,a)
GLuint vaoParticle, vboParticle; // for particles, using 9 floats (x,y,z, size,distance, r,g,b,a)
// UBOs and their data
gl3UniCommon_t uniCommonData;