| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- module three.gl.renderer;
- import three.scene;
- import three.camera;
- import three.viewport;
- import three.mesh;
- import std.string : toStringz;
- import std.exception : collectException;
- import std.experimental.logger;
- import three.gl.buffer;
- import three.gl.draw;
- import three.gl.sync;
- import three.gl.util;
- public import three.gl.renderTarget;
- enum maxVertices = 3*8500;
- enum maxIndices = 3*12636;
- enum maxPerInstanceParams = 1024;
- enum maxIndirectCommands = 3*1;
- //======================================================================================================================
- //
- //======================================================================================================================
- struct GBuffer {
- uint width;
- uint height;
- GLuint texturePosition;
- GLuint textureNormal;
- GLuint textureColor;
- GLuint textureDepthStencil;
- GLuint fbo;
-
- void construct(uint width, uint height) nothrow {
- this.width = width;
- this.height = height;
- glCheck!glGenTextures(1, &this.texturePosition);
- glCheck!glBindTexture(GL_TEXTURE_2D, this.texturePosition);
- glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- // glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- // glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- // glCheck!glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
- glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height);
- glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_FLOAT, null);
- glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-
- glCheck!glGenTextures(1, &this.textureNormal);
- glCheck!glBindTexture(GL_TEXTURE_2D, this.textureNormal);
- glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glCheck!glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- // glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- // glCheck!glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
- glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB10_A2, width, height);
- glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);
- glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-
- glCheck!glGenTextures(1, &this.textureColor);
- glCheck!glBindTexture(GL_TEXTURE_2D, this.textureColor);
- glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
- glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, null);
- glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-
- glCheck!glGenTextures(1, &this.textureDepthStencil);
- glCheck!glBindTexture(GL_TEXTURE_2D, this.textureDepthStencil);
- glCheck!glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height);
- glCheck!glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
- glCheck!glBindTexture(GL_TEXTURE_2D, 0);
-
- glCheck!glGenFramebuffers(1, &this.fbo);
- glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this.fbo);
- glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 0, GL_TEXTURE_2D, this.texturePosition, 0);
- glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1, GL_TEXTURE_2D, this.textureNormal, 0);
- glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 2, GL_TEXTURE_2D, this.textureColor, 0);
- glCheck!glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, this.textureDepthStencil, 0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- }
-
- void destruct() nothrow {
- glCheck!glDeleteFramebuffers(1, &this.fbo);
- glCheck!glDeleteTextures(1, &this.textureColor);
- glCheck!glDeleteTextures(1, &this.textureNormal);
- glCheck!glDeleteTextures(1, &this.texturePosition);
- }
- }
- //======================================================================================================================
- //
- //======================================================================================================================
- struct ShaderPipeline {
- GLuint pipeline;
- GLuint vertexShaderGeometryPass;
- GLuint fragmentShaderGeometryPass;
-
- void construct(string vertexShaderSource, string fragmentShaderSource) nothrow {
- glCheck!glGenProgramPipelines(1, &this.pipeline);
-
- auto szVertexSource = [vertexShaderSource.toStringz()];
- this.vertexShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_VERTEX_SHADER, 1, szVertexSource.ptr);
- int len;
- glCheck!glGetProgramiv(this.vertexShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
- if (len > 1) {
- char[] msg = new char[len];
- glCheck!glGetProgramInfoLog(this.vertexShaderGeometryPass, len, null, cast(char*) msg);
- log(cast(string)msg).collectException;
- }
-
- auto szFragmentSource = [fragmentShaderSource.toStringz()];
- this.fragmentShaderGeometryPass = glCheck!glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, szFragmentSource.ptr);
- // int len;
- glCheck!glGetProgramiv(this.fragmentShaderGeometryPass, GL_INFO_LOG_LENGTH , &len);
- if (len > 1) {
- char[] msg = new char[len];
- glCheck!glGetProgramInfoLog(this.fragmentShaderGeometryPass, len, null, cast(char*) msg);
- log(cast(string)msg).collectException;
- }
-
- glCheck!glUseProgramStages(this.pipeline, GL_VERTEX_SHADER_BIT, this.vertexShaderGeometryPass);
- glCheck!glUseProgramStages(this.pipeline, GL_FRAGMENT_SHADER_BIT, this.fragmentShaderGeometryPass);
-
- glCheck!glValidateProgramPipeline(this.pipeline);
- GLint status;
- glCheck!glGetProgramPipelineiv(this.pipeline, GL_VALIDATE_STATUS, &status);
- //TODO: add error handling
- assert(status != 0);
- }
-
- void destruct() nothrow {
- glDeleteProgramPipelines(1, &this.pipeline);
- }
- }
- enum vertexShaderSource = "
- #version 420 core
- layout(location = 0) in vec3 in_position;
- layout(location = 1) in vec3 in_normal;
- layout(location = 2) in vec4 in_color;
- layout(location = 3) in vec2 in_texcoord;
- //==============
- out vec2 _normal;
- out vec2 _texture;
- out vec3 _color;
- //==============
- out gl_PerVertex
- {
- vec4 gl_Position;
- };
- vec2 encode(vec3 n)
- {
- float f = sqrt(8*n.z+8);
- return n.xy / f + 0.5;
- }
-
- void main()
- {
- gl_Position = vec4(0.005 * in_position.x, 0.005 * in_position.y, 0.005* in_position.z, 1.0);
- _normal = encode(in_normal);
- _texture = in_texcoord;
- _color = in_color.xyz;
- };
- ";
- enum fragmentShaderSource = "
- #version 420 core
- //==============
- in vec2 _normal;
- in vec2 _texture;
- in vec3 _color;
- //==============
- layout(location = 0) out float depth;
- layout(location = 1) out vec4 normal;
- layout(location = 2) out vec4 color;
- void main()
- {
- depth = gl_FragCoord.z;
- normal.xy = _normal.xy;
- color.xyz = _color;
- }
- ";
- //======================================================================================================================
- //
- //======================================================================================================================
- struct Renderer {
- GBuffer gbuffer;
- ShaderPipeline shaderPipeline;
- GlArrayBuffer!VertexData vertexBuffer; // vertex data for all meshes
- GlElementArrayBuffer!IndexData indexBuffer; //index data for all meshes
- GlShaderStorageBuffer!GlDrawParameter perInstanceParamBuffer; // is filled with draw parameters for each instance each frame. shall be accessed as a ringbuffer
- GlDrawIndirectBuffer!GlDrawCommand drawCommandBuffer; // is filled with DrawElementsIndirectCommand for each mesh each frame. shall be accessed as a ringbuffer
- GlSyncManager vertexSyncManager;
- GlSyncManager indexSyncManager;
- GlSyncManager perInstanceParamSyncManager;
- GlSyncManager drawIndirectCommandSyncManager;
- size_t vertexRingbufferIndex = 0;
- size_t indexRingbufferIndex = 0;
- size_t perInstanceParamRingbufferIndex = 0;
- size_t drawCommandRingbufferIndex = 0;
-
- void construct(uint width, uint height) {
- GLbitfield createFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;//TODO: ?? | GL_MAP_DYNAMIC_STORAGE_BIT;
- GLbitfield mapFlags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT;
-
- this.gbuffer.construct(width, height);
- this.shaderPipeline.construct(vertexShaderSource, fragmentShaderSource);
- this.vertexBuffer.construct(maxVertices, createFlags, mapFlags);
- this.indexBuffer.construct(maxIndices, createFlags, mapFlags);
- this.perInstanceParamBuffer.construct(maxPerInstanceParams, createFlags, mapFlags);
- this.drawCommandBuffer.construct(maxIndirectCommands, createFlags, mapFlags);
- this.vertexSyncManager.construct();
- this.indexSyncManager.construct();
- this.perInstanceParamSyncManager.construct();
- this.drawIndirectCommandSyncManager.construct();
- glCheck!glEnableVertexAttribArray(0);
- glCheck!glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)0 );
- glCheck!glEnableVertexAttribArray(1);
- glCheck!glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)3 );
- glCheck!glEnableVertexAttribArray(2);
- glCheck!glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)6 );
- glCheck!glEnableVertexAttribArray(3);
- glCheck!glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, VertexData.sizeof, cast(GLvoid*)10 );
- }
-
- void destruct() {
- this.drawIndirectCommandSyncManager.destruct();
- this.perInstanceParamSyncManager.destruct();
- this.indexSyncManager.destruct();
- this.vertexSyncManager.destruct();
- this.drawCommandBuffer.destruct();
- this.perInstanceParamBuffer.destruct();
- this.indexBuffer.destruct();
- this.vertexBuffer.destruct();
- this.shaderPipeline.destruct();
- this.gbuffer.destruct();
- }
- void renderOneFrame(ref Scene scene, ref Camera camera, ref GlRenderTarget renderTarget, ref Viewport viewport) {
- assert(vertexBuffer.length >= 3 * scene.modelData.vertexCount);
- assert(indexBuffer.length >= 3 * scene.modelData.indexCount);
- assert(drawCommandBuffer.length >= 3 * scene.modelData.meshCount);
- // calc if we have to wrap our buffer
- if(vertexRingbufferIndex + scene.modelData.vertexCount > this.vertexBuffer.length) {
- vertexRingbufferIndex = 0;
- }
-
- if(indexRingbufferIndex + scene.modelData.indexCount > this.indexBuffer.length) {
- indexRingbufferIndex = 0;
- }
-
- if(drawCommandRingbufferIndex + scene.modelData.meshCount > this.drawCommandBuffer.length) {
- drawCommandRingbufferIndex = 0;
- }
- // wait until GPU has finished rendereing from our desired buffer destination
- this.vertexSyncManager.waitForLockedRange(vertexRingbufferIndex, scene.modelData.vertexCount);
- // log("vertexSyncManager: ", vertexRingbufferIndex, " ", scene.modelData.vertexCount);
- this.indexSyncManager.waitForLockedRange(indexRingbufferIndex, scene.modelData.indexCount);
- // log("indexSyncManager: ", indexRingbufferIndex, " ", scene.modelData.indexCount);
- this.drawIndirectCommandSyncManager.waitForLockedRange(drawCommandRingbufferIndex, scene.modelData.meshCount);
- // log("drawIndirectCommandSyncManager: ", drawCommandRingbufferIndex, " ", scene.modelData.meshCount);
- // bind buffers
- this.vertexBuffer.bind(); scope(exit) this.vertexBuffer.unbind();
- this.indexBuffer.bind(); scope(exit) this.indexBuffer.unbind();
- this.perInstanceParamBuffer.bind(); scope(exit) this.perInstanceParamBuffer.unbind();
- this.drawCommandBuffer.bind(); scope(exit) this.drawCommandBuffer.unbind();
- //bind gbuffer
- glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gbuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- GLenum[] drawBuffers = [GL_COLOR_ATTACHMENT0 + 0, GL_COLOR_ATTACHMENT0 + 1, GL_COLOR_ATTACHMENT0 + 2];
- glCheck!glDrawBuffers(drawBuffers.length, drawBuffers.ptr); scope(exit) glCheck!glDrawBuffer(GL_NONE);
-
- // clear scene
- glCheck!glEnable(GL_DEPTH_TEST);
- glCheck!glDepthFunc(GL_LEQUAL);
- glCheck!glDisable(GL_SCISSOR_TEST);
- glCheck!glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
- glCheck!glDepthMask(GL_TRUE); // enable depth mask _before_ glClear ing the depth buffer!
- glCheck!glStencilMask(0xFFFFFFFF);
- glCheck!glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
- glCheck!glClearDepth(1.0f);
- glCheck!glClearColor(0, 0.3, 0, 1);
-
- glCheck!glViewport(0, 0, this.gbuffer.width, this.gbuffer.height);
- // backup the indices, we'll need it for our draw command and locking
- auto curVertexRingbufferIndex = vertexRingbufferIndex;
- auto curIndexRingbufferIndex = indexRingbufferIndex;
- auto curDrawCommandRingbufferIndex = drawCommandRingbufferIndex;
- // upload data to our buffers
- // foreach(model; scene.modelData) {
- // foreach(meshData; model.meshData) {
- foreach(meshData; scene.modelData.meshData) {
- // upload vertex data
- this.vertexBuffer.data[vertexRingbufferIndex .. vertexRingbufferIndex + meshData.vertexData.length] = meshData.vertexData[0 .. meshData.vertexData.length];
- // upload index data
- this.indexBuffer.data[indexRingbufferIndex .. indexRingbufferIndex + meshData.indexData.length] = meshData.indexData[0 .. meshData.indexData.length];
- // draw command data
- this.drawCommandBuffer.data[drawCommandRingbufferIndex] = GlDrawCommand(meshData.indexData.length, 1, indexRingbufferIndex, vertexRingbufferIndex, 0);
- log(this.drawCommandBuffer.data[drawCommandRingbufferIndex]);
- // advance ringbuffers
- vertexRingbufferIndex += meshData.vertexData.length;
- indexRingbufferIndex += meshData.indexData.length;
- ++drawCommandRingbufferIndex;
- }
- // }
- // }
-
- // bind pipeline
- glCheck!glBindProgramPipeline(shaderPipeline.pipeline); scope(exit) glCheck!glBindProgramPipeline(0);
- // draw
- glCheck!glMultiDrawElementsIndirect(GL_TRIANGLES, toGlType!(this.indexBuffer.ValueType), cast(const void*)(curDrawCommandRingbufferIndex * GlDrawCommand.sizeof), scene.modelData.meshCount, 0);
- // lock ranges
- this.vertexSyncManager.lockRange(curVertexRingbufferIndex, scene.modelData.vertexCount);
- this.indexSyncManager.lockRange(curIndexRingbufferIndex, scene.modelData.indexCount);
- this.drawIndirectCommandSyncManager.lockRange(curDrawCommandRingbufferIndex, scene.modelData.meshCount);
- }
-
- debug {
- void blitGBufferToScreen() {
- glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, this.gbuffer.fbo); scope(exit) glCheck!glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
-
- GLsizei width = this.gbuffer.width;
- GLsizei height = this.gbuffer.height;
-
- scope(exit) glCheck!glReadBuffer(GL_NONE);
-
- glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 0);
- glCheck!glBlitFramebuffer(0, 0, width, height, 0, height-300, 400, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
- glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 1);
- glCheck!glBlitFramebuffer(0, 0, width, height, 0, 0, 400, 300, GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
- glCheck!glReadBuffer(GL_COLOR_ATTACHMENT0 + 2);
- glCheck!glBlitFramebuffer(0, 0, width, height, width-400, height-300, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
- }
- }
- }
|