Rain: Use transform feedback to decouple simulation from rendering
It's the base for a future gpu based particle system git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@14806 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#version 130
|
||||
uniform sampler2D tex;
|
||||
uniform float bdiscard = 0.0;
|
||||
uniform sampler2D normals_and_depth;
|
||||
uniform mat4 invproj;
|
||||
uniform vec2 screen;
|
||||
@@ -14,6 +16,8 @@ void main()
|
||||
EnvPos /= EnvPos.w;
|
||||
float len = dot(vec3(1.0), abs(texture2D(normals_and_depth, xy).xyz));
|
||||
float alpha = (len < 0.2) ? 1. : clamp((EnvPos.z - FragmentPos.z) * 0.3, 0., 1.);
|
||||
gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);
|
||||
if (bdiscard < 0.5)
|
||||
discard;
|
||||
gl_FragColor = texture2D(tex, gl_PointCoord.xy);
|
||||
gl_FragColor.a *= alpha;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,12 @@
|
||||
uniform float screenw;
|
||||
uniform float time;
|
||||
uniform mat4 viewm;
|
||||
uniform vec3 campos;
|
||||
|
||||
void main()
|
||||
{
|
||||
const float size = 0.5;
|
||||
|
||||
// This simulation will run accurately for a bit under five days.
|
||||
vec4 start = gl_Vertex;
|
||||
start.y -= time;
|
||||
|
||||
// How many times has it fell?
|
||||
float count = floor(start.y / 24.0);
|
||||
start.x += sin(count);
|
||||
start.z += cos(count);
|
||||
|
||||
vec2 signs = sign(start.xz);
|
||||
start.xz = mod(start.xz, 17.5) * signs;
|
||||
|
||||
start.y = mod(start.y, 24.0) - 3.0;
|
||||
|
||||
start.xyz += campos;
|
||||
|
||||
vec4 eyepos = viewm * start;
|
||||
vec4 eyepos = gl_Vertex;
|
||||
vec4 projCorner = gl_ProjectionMatrix * vec4(vec2(size), eyepos.z, eyepos.w);
|
||||
|
||||
gl_PointSize = screenw * projCorner.x / projCorner.w;
|
||||
gl_Position = gl_ProjectionMatrix * eyepos;
|
||||
|
||||
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
|
||||
}
|
||||
|
||||
27
data/shaders/rainsim.vert
Normal file
27
data/shaders/rainsim.vert
Normal file
@@ -0,0 +1,27 @@
|
||||
uniform float time;
|
||||
uniform vec3 campos;
|
||||
uniform mat4 viewm;
|
||||
|
||||
in vec3 initialPosition;
|
||||
out vec3 currentPosition;
|
||||
|
||||
void main()
|
||||
{
|
||||
// This simulation will run accurately for a bit under five days.
|
||||
vec4 start = vec4(initialPosition, 1.0);
|
||||
start.y -= time;
|
||||
|
||||
// How many times has it fell?
|
||||
float count = floor(start.y / 24.0);
|
||||
start.x += sin(count);
|
||||
start.z += cos(count);
|
||||
|
||||
vec2 signs = sign(start.xz);
|
||||
start.xz = mod(start.xz, 17.5) * signs;
|
||||
|
||||
start.y = mod(start.y, 24.0) - 3.0;
|
||||
|
||||
start.xyz += campos;
|
||||
|
||||
currentPosition = (viewm * start).xyz;
|
||||
}
|
||||
@@ -37,9 +37,260 @@ using namespace video;
|
||||
using namespace scene;
|
||||
using namespace core;
|
||||
|
||||
// OPENGL PARTICLE SYSTEM
|
||||
#include <ICameraSceneNode.h>
|
||||
#include "../source/Irrlicht/COpenGLExtensionHandler.h"
|
||||
#include "io/file_manager.hpp"
|
||||
|
||||
#ifdef _IRR_WINDOWS_API_
|
||||
#define IRR_OGL_LOAD_EXTENSION(X) wglGetProcAddress(reinterpret_cast<const char*>(X))
|
||||
#else
|
||||
#include <GL/glx.h>
|
||||
#define IRR_OGL_LOAD_EXTENSION(X) glXGetProcAddress(reinterpret_cast<const GLubyte*>(X))
|
||||
#endif
|
||||
|
||||
|
||||
PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks;
|
||||
PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback;
|
||||
PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback;
|
||||
PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
|
||||
PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
|
||||
PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
|
||||
PFNGLBINDBUFFERBASEPROC glBindBufferBase;
|
||||
PFNGLGENBUFFERSPROC glGenBuffers;
|
||||
PFNGLBINDBUFFERPROC glBindBuffer;
|
||||
PFNGLBUFFERDATAPROC glBufferData;
|
||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
|
||||
PFNGLCREATESHADERPROC glCreateShader;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource;
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram;
|
||||
PFNGLATTACHSHADERPROC glAttachShader;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram;
|
||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
|
||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
|
||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
|
||||
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
|
||||
PFNGLUNIFORM1FPROC glUniform1f;
|
||||
PFNGLUNIFORM3FPROC glUniform3f;
|
||||
PFNGLDELETESHADERPROC glDeleteShader;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
||||
#ifdef _IRR_WINDOWS_API_
|
||||
PFNGLACTIVETEXTUREPROC glActiveTexture;
|
||||
#endif
|
||||
PFNGLUNIFORM2FPROC glUniform2f;
|
||||
PFNGLUNIFORM1IPROC glUniform1i;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
||||
|
||||
void initGL()
|
||||
{
|
||||
glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)IRR_OGL_LOAD_EXTENSION("glGenTransformFeedbacks");
|
||||
glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glBindTransformFeedback");
|
||||
glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glDrawTransformFeedback");
|
||||
glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glBeginTransformFeedback");
|
||||
glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glEndTransformFeedback");
|
||||
glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)IRR_OGL_LOAD_EXTENSION("glBindBufferBase");
|
||||
glGenBuffers = (PFNGLGENBUFFERSPROC)IRR_OGL_LOAD_EXTENSION("glGenBuffers");
|
||||
glBindBuffer = (PFNGLBINDBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glBindBuffer");
|
||||
glBufferData = (PFNGLBUFFERDATAPROC)IRR_OGL_LOAD_EXTENSION("glBufferData");
|
||||
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)IRR_OGL_LOAD_EXTENSION("glVertexAttribPointer");
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)IRR_OGL_LOAD_EXTENSION("glCreateShader");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)IRR_OGL_LOAD_EXTENSION("glCompileShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)IRR_OGL_LOAD_EXTENSION("glShaderSource");
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glCreateProgram");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)IRR_OGL_LOAD_EXTENSION("glAttachShader");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glLinkProgram");
|
||||
glUseProgram = (PFNGLUSEPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glUseProgram");
|
||||
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)IRR_OGL_LOAD_EXTENSION("glEnableVertexAttribArray");
|
||||
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)IRR_OGL_LOAD_EXTENSION("glGetUniformLocation");
|
||||
glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)IRR_OGL_LOAD_EXTENSION("glUniformMatrix4fv");
|
||||
glUniform1f = (PFNGLUNIFORM1FPROC)IRR_OGL_LOAD_EXTENSION("glUniform1f");
|
||||
glUniform3f = (PFNGLUNIFORM3FPROC)IRR_OGL_LOAD_EXTENSION("glUniform3f");
|
||||
glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)IRR_OGL_LOAD_EXTENSION("glDisableVertexAttribArray");
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)IRR_OGL_LOAD_EXTENSION("glDeleteShader");
|
||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)IRR_OGL_LOAD_EXTENSION("glGetShaderiv");
|
||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)IRR_OGL_LOAD_EXTENSION("glGetShaderInfoLog");
|
||||
#ifdef _IRR_WINDOWS_API_
|
||||
glActiveTexture = (PFNGLACTIVETEXTUREPROC)IRR_OGL_LOAD_EXTENSION("glActiveTexture");
|
||||
#endif
|
||||
glUniform2f = (PFNGLUNIFORM2FPROC)IRR_OGL_LOAD_EXTENSION("glUniform2f");
|
||||
glUniform1i = (PFNGLUNIFORM1IPROC)IRR_OGL_LOAD_EXTENSION("glUniform1i");
|
||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)IRR_OGL_LOAD_EXTENSION("glGetProgramiv");
|
||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)IRR_OGL_LOAD_EXTENSION("glGetProgramInfoLog");
|
||||
glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)IRR_OGL_LOAD_EXTENSION("glTransformFeedbackVaryings");
|
||||
}
|
||||
|
||||
// Mostly from shader tutorial
|
||||
GLuint LoadShader(const char * file, unsigned type) {
|
||||
GLuint Id = glCreateShader(type);
|
||||
std::string Code;
|
||||
std::ifstream Stream(file, std::ios::in);
|
||||
if (Stream.is_open())
|
||||
{
|
||||
std::string Line = "";
|
||||
while (getline(Stream, Line))
|
||||
Code += "\n" + Line;
|
||||
Stream.close();
|
||||
}
|
||||
GLint Result = GL_FALSE;
|
||||
int InfoLogLength;
|
||||
printf("Compiling shader : %s\n", file);
|
||||
char const * SourcePointer = Code.c_str();
|
||||
int length = strlen(SourcePointer);
|
||||
glShaderSource(Id, 1, &SourcePointer, &length);
|
||||
glCompileShader(Id);
|
||||
|
||||
glGetShaderiv(Id, GL_COMPILE_STATUS, &Result);
|
||||
if (Result == GL_FALSE) {
|
||||
glGetShaderiv(Id, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
char *ErrorMessage = new char[InfoLogLength];
|
||||
glGetShaderInfoLog(Id, InfoLogLength, NULL, ErrorMessage);
|
||||
printf(ErrorMessage);
|
||||
delete[] ErrorMessage;
|
||||
}
|
||||
|
||||
return Id;
|
||||
}
|
||||
|
||||
GLuint LoadProgram(const char * vertex_file_path, const char * fragment_file_path){
|
||||
GLuint VertexShaderID = LoadShader(vertex_file_path, GL_VERTEX_SHADER);
|
||||
GLuint FragmentShaderID = LoadShader(fragment_file_path, GL_FRAGMENT_SHADER);
|
||||
|
||||
GLuint ProgramID = glCreateProgram();
|
||||
glAttachShader(ProgramID, VertexShaderID);
|
||||
glAttachShader(ProgramID, FragmentShaderID);
|
||||
glLinkProgram(ProgramID);
|
||||
|
||||
GLint Result = GL_FALSE;
|
||||
int InfoLogLength;
|
||||
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
|
||||
if (Result == GL_FALSE) {
|
||||
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
char *ErrorMessage = new char[InfoLogLength];
|
||||
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, ErrorMessage);
|
||||
printf(ErrorMessage);
|
||||
delete[] ErrorMessage;
|
||||
}
|
||||
|
||||
glDeleteShader(VertexShaderID);
|
||||
glDeleteShader(FragmentShaderID);
|
||||
|
||||
return ProgramID;
|
||||
}
|
||||
|
||||
GLuint getTextureGLuint(ITexture *tex) { return static_cast<const COpenGLTexture*>(tex)->getOpenGLTextureName(); }
|
||||
|
||||
void bindUniformToTextureUnit(GLuint program, const char * name, GLuint texid, unsigned textureUnit) {
|
||||
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||
glBindTexture(GL_TEXTURE_2D, texid);
|
||||
GLuint location = glGetUniformLocation(program, name);
|
||||
glUniform1i(location, textureUnit);
|
||||
}
|
||||
|
||||
class GPUParticle {
|
||||
private:
|
||||
GLuint SimulationProgram, RenderProgram, tfb_vertex_buffer[2];
|
||||
GLuint texture;
|
||||
GLuint normal_and_depth;
|
||||
unsigned count;
|
||||
public:
|
||||
GPUParticle(unsigned c, float *initialSamples, GLuint tex, GLuint rtt) :
|
||||
count(c), texture(tex), normal_and_depth(rtt) {
|
||||
initGL();
|
||||
glGenBuffers(2, tfb_vertex_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_vertex_buffer[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 3 * count * sizeof(float), initialSamples, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_vertex_buffer[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 3 * count * sizeof(float), 0, GL_STREAM_DRAW);
|
||||
|
||||
RenderProgram = LoadProgram(file_manager->getAsset("shaders/rain.vert").c_str(), file_manager->getAsset("shaders/rain.frag").c_str());
|
||||
GLuint SimShader = LoadShader(file_manager->getAsset("shaders/rainsim.vert").c_str(), GL_VERTEX_SHADER);
|
||||
SimulationProgram = glCreateProgram();
|
||||
glAttachShader(SimulationProgram, SimShader);
|
||||
const char *varying[] = { "currentPosition" };
|
||||
glTransformFeedbackVaryings(SimulationProgram, 1, varying, GL_INTERLEAVED_ATTRIBS);
|
||||
glLinkProgram(SimulationProgram);
|
||||
|
||||
GLint Result = GL_FALSE;
|
||||
int InfoLogLength;
|
||||
glGetProgramiv(SimulationProgram, GL_LINK_STATUS, &Result);
|
||||
if (Result == GL_FALSE) {
|
||||
glGetProgramiv(SimulationProgram, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
char *ErrorMessage = new char[InfoLogLength];
|
||||
glGetProgramInfoLog(SimulationProgram, InfoLogLength, NULL, ErrorMessage);
|
||||
printf(ErrorMessage);
|
||||
delete[] ErrorMessage;
|
||||
}
|
||||
glDeleteShader(SimShader);
|
||||
}
|
||||
|
||||
void simulate() {
|
||||
glUseProgram(SimulationProgram);
|
||||
const float time = irr_driver->getDevice()->getTimer()->getTime() / 90.0f;
|
||||
const matrix4 viewm = irr_driver->getVideoDriver()->getTransform(ETS_VIEW);
|
||||
const vector3df campos = irr_driver->getSceneManager()->getActiveCamera()->getPosition();
|
||||
|
||||
glEnable(GL_RASTERIZER_DISCARD);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_vertex_buffer[0]);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfb_vertex_buffer[1]);
|
||||
GLuint lcampos = glGetUniformLocation(SimulationProgram, "campos");
|
||||
GLuint lviewm = glGetUniformLocation(SimulationProgram, "viewm");
|
||||
GLuint ltime = glGetUniformLocation(SimulationProgram, "time");
|
||||
glUniformMatrix4fv(lviewm, 1, GL_FALSE, viewm.pointer());
|
||||
glUniform1f(ltime, time);
|
||||
glUniform3f(lcampos, campos.X, campos.Y, campos.Z);
|
||||
glBeginTransformFeedback(GL_POINTS);
|
||||
glDrawArrays(GL_POINTS, 0, count);
|
||||
glEndTransformFeedback();
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
|
||||
void render() {
|
||||
const float screenw = (float)UserConfigParams::m_width;
|
||||
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
glUseProgram(RenderProgram);
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, tfb_vertex_buffer[1]);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
float screen[2] = { (float)UserConfigParams::m_width,
|
||||
(float)UserConfigParams::m_height };
|
||||
matrix4 invproj = irr_driver->getVideoDriver()->getTransform(ETS_PROJECTION);
|
||||
invproj.makeInverse();
|
||||
|
||||
GLuint lscreenw = glGetUniformLocation(RenderProgram, "screenw");
|
||||
GLuint bdiscard = glGetUniformLocation(RenderProgram, "bdiscard");
|
||||
GLuint lscreen = glGetUniformLocation(RenderProgram, "screen");
|
||||
GLuint linvproj = glGetUniformLocation(RenderProgram, "invproj");
|
||||
|
||||
bindUniformToTextureUnit(RenderProgram, "tex", texture, 0);
|
||||
bindUniformToTextureUnit(RenderProgram, "normals_and_depth", normal_and_depth, 1);
|
||||
|
||||
|
||||
glUniformMatrix4fv(linvproj, 1, GL_FALSE, invproj.pointer());
|
||||
glUniform2f(lscreen, screen[0], screen[1]);
|
||||
glUniform1f(bdiscard, 1.0);
|
||||
glUniform1f(lscreenw, screenw);
|
||||
glDrawArrays(GL_POINTS, 0, count);
|
||||
glDisableVertexAttribArray(0);
|
||||
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
}
|
||||
};
|
||||
// END OPENGL PARTICLE SYSTEM
|
||||
|
||||
// The actual rain node
|
||||
class RainNode: public scene::ISceneNode
|
||||
{
|
||||
private:
|
||||
GPUParticle *gpupart;
|
||||
public:
|
||||
RainNode(scene::ISceneManager* mgr, ITexture *tex)
|
||||
: scene::ISceneNode(0, mgr, -1)
|
||||
@@ -68,7 +319,7 @@ public:
|
||||
buf.setHardwareMappingHint(EHM_STATIC);
|
||||
|
||||
u32 i;
|
||||
float x, y, z;
|
||||
float x, y, z, vertices[7500];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
x = ((rand() % area) - area/2) / 100.0f;
|
||||
@@ -77,7 +328,11 @@ public:
|
||||
|
||||
buf.Indices[i] = i;
|
||||
buf.Vertices[i] = S3DVertex(x, y, z, 0, 0, 0, SColor(255, 255, 0, 0), 0, 0);
|
||||
vertices[3 * i] = x;
|
||||
vertices[3 * i + 1] = y;
|
||||
vertices[3 * i + 2] = z;
|
||||
}
|
||||
gpupart = new GPUParticle(count, vertices, getTextureGLuint(mat.getTexture(0)), getTextureGLuint(irr_driver->getRTT(RTT_NORMAL_AND_DEPTH)));
|
||||
|
||||
box.addInternalPoint(vector3df((float)(-area/2)));
|
||||
box.addInternalPoint(vector3df((float)( area/2)));
|
||||
@@ -85,10 +340,14 @@ public:
|
||||
|
||||
~RainNode()
|
||||
{
|
||||
delete gpupart;
|
||||
}
|
||||
|
||||
virtual void render()
|
||||
{
|
||||
gpupart->simulate();
|
||||
gpupart->render();
|
||||
// We need to let irrlicht render something so it updates its internal states
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
|
||||
IVideoDriver * const drv = irr_driver->getVideoDriver();
|
||||
|
||||
Reference in New Issue
Block a user