diff --git a/src/k3.c b/src/k3.c index d0f31ab..d9241cb 100644 --- a/src/k3.c +++ b/src/k3.c @@ -2744,46 +2744,81 @@ struct k3ARBFP *k3ProgramARBFP(const char *src) { return (struct k3ARBFP*) (uintptr_t) p; } -struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth) { +struct k3Offscreen *k3OffscreenCreateMultisampled(struct k3Tex *diffuse, struct k3Tex *depth, uint8_t samples) { k3Log(k3_INFO, "Init %sFBO", !diffuse && depth ? "depth-only " : ""); - GLuint fbo = 0; - if(GLAD_GL_EXT_framebuffer_object) { - glGenFramebuffersEXT(1, &fbo); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); - - if(diffuse) { - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(diffuse), 0); - } else { - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - } - - if(depth) { - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(depth), 0); - } - - if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) { - k3Log(k3_WARN, "Framebuffer incomplete"); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glDrawBuffer(GL_BACK); // XXX: this should not be necessary - } else { + if(samples && (!GLAD_GL_EXT_framebuffer_multisample || !GLAD_GL_EXT_framebuffer_blit)) { + samples = 0; + k3Log(k3_WARN, "Multisampled offscreens not supported."); + } + + if(!GLAD_GL_EXT_framebuffer_object) { k3Log(k3_ERR, "Non-FBO offscreens not implemented"); return NULL; } - struct k3Offscreen *ret = malloc(sizeof(*ret)); + GLuint fbo = 0; + glGenFramebuffersEXT(1, &fbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); + + struct k3Offscreen *ret = calloc(1, sizeof(*ret)); ret->fbo = fbo; ret->diffuse = diffuse; ret->depth = depth; + ret->multisampling.samples = samples; + + if(diffuse) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(diffuse), 0); + } else { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + + if(depth) { + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(depth), 0); + } + + if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) { + k3Log(k3_WARN, "Framebuffer incomplete"); + } + + if(samples) { + GLuint msfbo; + glGenFramebuffersEXT(1, &msfbo); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, msfbo); + + ret->multisampling.fbo = msfbo; + + if(diffuse) { + glGenRenderbuffersEXT(1, &ret->multisampling.rboDiffuse); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, ret->multisampling.rboDiffuse); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, diffuse->glInternalFormat, diffuse->szX, diffuse->szY); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, ret->multisampling.rboDiffuse); + } + + if(depth) { + glGenRenderbuffersEXT(1, &ret->multisampling.rboDepth); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, ret->multisampling.rboDepth); + glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, depth->glInternalFormat, depth->szX, depth->szY); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ret->multisampling.rboDepth); + } + } + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); // XXX: this should not be necessary + return ret; } +struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth) { + return k3OffscreenCreateMultisampled(diffuse, depth, 0); +} + void k3BeginOffscreen(struct k3Offscreen *offscr) { if(GLAD_GL_EXT_framebuffer_object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, offscr->fbo); + GLuint fbo = offscr->multisampling.samples > 0 ? offscr->multisampling.fbo : offscr->fbo; + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); struct k3Tex *t = offscr->diffuse ? offscr->diffuse : offscr->depth; glViewport(0, 0, k3TexSzX(t), k3TexSzY(t)); @@ -2794,6 +2829,12 @@ void k3BeginOffscreen(struct k3Offscreen *offscr) { void k3EndOffscreen(struct k3Offscreen *offscr) { if(GLAD_GL_EXT_framebuffer_object) { + if(offscr->multisampling.samples) { + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, offscr->fbo); + + glBlitFramebufferEXT(0, 0, offscr->diffuse->szX, offscr->diffuse->szY, 0, 0, offscr->diffuse->szX, offscr->diffuse->szY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glViewport(0, 0, MainWidth, MainHeight); } else { @@ -2809,7 +2850,9 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect, if(GLAD_GL_EXT_framebuffer_object) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(offscr->diffuse)); + struct k3Tex *tex = offscr->diffuse ? offscr->diffuse : offscr->depth; + + glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(tex)); if(additive) { glEnable(GL_BLEND); @@ -2856,7 +2899,7 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect, } GLint uSz = glGetUniformLocation(GL_FROM_K3GLSL(basicBlitProgram), "u_sz"); - glUniform2f(uSz, k3TexSzX(offscr->diffuse), k3TexSzY(offscr->diffuse)); + glUniform2f(uSz, k3TexSzX(tex), k3TexSzY(tex)); glDrawArrays(GL_TRIANGLES, 0, 3); } else { @@ -2864,8 +2907,8 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect, glUseProgramObjectARB(effect == k3_GLSL ? GL_FROM_K3GLSL(program) : 0); } - float bleedW = 1.0f / k3TexSzX(offscr->diffuse); - float bleedH = 1.0f / k3TexSzY(offscr->diffuse); + float bleedW = 1.0f / k3TexSzX(tex); + float bleedH = 1.0f / k3TexSzY(tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); diff --git a/src/k3.h b/src/k3.h index ba452f3..9e4cfb3 100644 --- a/src/k3.h +++ b/src/k3.h @@ -190,6 +190,7 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull); void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr); struct k3Offscreen; +struct k3Offscreen *k3OffscreenCreateMultisampled(struct k3Tex *diffuse, struct k3Tex *depth, uint8_t samples); struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth); void k3BeginOffscreen(struct k3Offscreen*); void k3EndOffscreen(struct k3Offscreen*); diff --git a/src/k3_internal.h b/src/k3_internal.h index 318d3d9..9948e8f 100644 --- a/src/k3_internal.h +++ b/src/k3_internal.h @@ -45,6 +45,13 @@ struct k3Offscreen { GLuint fbo; struct k3Tex *diffuse; struct k3Tex *depth; + + struct { + uint8_t samples; + GLuint fbo; + GLuint rboDiffuse; + GLuint rboDepth; + } multisampling; }; struct k3Mdl {