This refactor modernise the use of framebuffers. It also touches a lot of files so breaking down changes we have: - GPUTexture: Allow textures to be attached to more than one GPUFrameBuffer. This allows to create and configure more FBO without the need to attach and detach texture at drawing time. - GPUFrameBuffer: The wrapper starts to mimic opengl a bit closer. This allows to configure the framebuffer inside a context other than the one that will be rendering the framebuffer. We do the actual configuration when binding the FBO. We also Keep track of config validity and save drawbuffers state in the FBO. We remove the different bind/unbind functions. These make little sense now that we have separate contexts. - DRWFrameBuffer: We replace DRW_framebuffer functions by GPU_framebuffer ones to avoid another layer of abstraction. We move the DRW convenience functions to GPUFramebuffer instead and even add new ones. The MACRO GPU_framebuffer_ensure_config is pretty much all you need to create and config a GPUFramebuffer. - DRWTexture: Due to the removal of DRWFrameBuffer, we needed to create functions to create textures for thoses framebuffers. Pool textures are now using default texture parameters for the texture type asked. - DRWManager: Make sure no framebuffer object is bound when doing cache filling. - GPUViewport: Add new color_only_fb and depth_only_fb along with FB API usage update. This let draw engines render to color/depth only target and without the need to attach/detach textures. - WM_window: Assert when a framebuffer is bound when changing context. This balance the fact we are not track ogl context inside GPUFramebuffer. - Eevee, Clay, Mode engines: Update to new API. This comes with a lot of code simplification. This also come with some cleanups in some engine codes.
461 lines
12 KiB
C
461 lines
12 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2006 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Brecht Van Lommel, Clément Foucault.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/gpu/intern/gpu_lamp.c
|
|
* \ingroup gpu
|
|
*
|
|
* Manages Opengl lights.
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_group.h"
|
|
|
|
#include "GPU_framebuffer.h"
|
|
#include "GPU_glew.h"
|
|
#include "GPU_lamp.h"
|
|
#include "GPU_material.h"
|
|
#include "GPU_shader.h"
|
|
#include "GPU_texture.h"
|
|
#include "GPU_batch.h"
|
|
|
|
#include "gpu_lamp_private.h"
|
|
|
|
bool GPU_lamp_visible(GPULamp *lamp, Material *ma)
|
|
{
|
|
if (lamp->hide)
|
|
return false;
|
|
else if (ma && ma->group)
|
|
return BKE_group_object_exists(ma->group, lamp->ob);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
static void gpu_lamp_calc_winmat(GPULamp *lamp)
|
|
{
|
|
float temp, angle, pixsize, wsize;
|
|
|
|
if (lamp->type == LA_SUN) {
|
|
wsize = lamp->la->shadow_frustum_size;
|
|
orthographic_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend);
|
|
}
|
|
else if (lamp->type == LA_SPOT) {
|
|
angle = saacos(lamp->spotsi);
|
|
temp = 0.5f * lamp->size * cosf(angle) / sinf(angle);
|
|
pixsize = lamp->d / temp;
|
|
wsize = pixsize * 0.5f * lamp->size;
|
|
/* compute shadows according to X and Y scaling factors */
|
|
perspective_m4(
|
|
lamp->winmat,
|
|
-wsize * lamp->spotvec[0], wsize * lamp->spotvec[0],
|
|
-wsize * lamp->spotvec[1], wsize * lamp->spotvec[1],
|
|
lamp->d, lamp->clipend);
|
|
}
|
|
}
|
|
|
|
void GPU_lamp_update(GPULamp *lamp, int lay, int hide, float obmat[4][4])
|
|
{
|
|
float mat[4][4];
|
|
float obmat_scale[3];
|
|
|
|
lamp->lay = lay;
|
|
lamp->hide = hide;
|
|
|
|
normalize_m4_m4_ex(mat, obmat, obmat_scale);
|
|
|
|
copy_v3_v3(lamp->vec, mat[2]);
|
|
copy_v3_v3(lamp->co, mat[3]);
|
|
copy_m4_m4(lamp->obmat, mat);
|
|
invert_m4_m4(lamp->imat, mat);
|
|
|
|
if (lamp->type == LA_SPOT) {
|
|
/* update spotlamp scale on X and Y axis */
|
|
lamp->spotvec[0] = obmat_scale[0] / obmat_scale[2];
|
|
lamp->spotvec[1] = obmat_scale[1] / obmat_scale[2];
|
|
}
|
|
|
|
if (GPU_lamp_has_shadow_buffer(lamp)) {
|
|
/* makeshadowbuf */
|
|
gpu_lamp_calc_winmat(lamp);
|
|
}
|
|
}
|
|
|
|
void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float energy)
|
|
{
|
|
lamp->energy = energy;
|
|
if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
|
|
|
|
lamp->col[0] = r;
|
|
lamp->col[1] = g;
|
|
lamp->col[2] = b;
|
|
}
|
|
|
|
void GPU_lamp_update_distance(GPULamp *lamp, float distance, float att1, float att2,
|
|
float coeff_const, float coeff_lin, float coeff_quad)
|
|
{
|
|
lamp->dist = distance;
|
|
lamp->att1 = att1;
|
|
lamp->att2 = att2;
|
|
lamp->coeff_const = coeff_const;
|
|
lamp->coeff_lin = coeff_lin;
|
|
lamp->coeff_quad = coeff_quad;
|
|
}
|
|
|
|
void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend)
|
|
{
|
|
lamp->spotsi = cosf(spotsize * 0.5f);
|
|
lamp->spotbl = (1.0f - lamp->spotsi) * spotblend;
|
|
}
|
|
|
|
static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *la, GPULamp *lamp)
|
|
{
|
|
lamp->scene = scene;
|
|
lamp->ob = ob;
|
|
lamp->par = par;
|
|
lamp->la = la;
|
|
|
|
/* add_render_lamp */
|
|
lamp->mode = la->mode;
|
|
lamp->type = la->type;
|
|
|
|
lamp->energy = la->energy;
|
|
if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
|
|
|
|
lamp->col[0] = la->r;
|
|
lamp->col[1] = la->g;
|
|
lamp->col[2] = la->b;
|
|
|
|
GPU_lamp_update(lamp, ob->lay, (ob->restrictflag & OB_RESTRICT_RENDER), ob->obmat);
|
|
|
|
lamp->spotsi = la->spotsize;
|
|
if (lamp->mode & LA_HALO)
|
|
if (lamp->spotsi > DEG2RADF(170.0f))
|
|
lamp->spotsi = DEG2RADF(170.0f);
|
|
lamp->spotsi = cosf(lamp->spotsi * 0.5f);
|
|
lamp->spotbl = (1.0f - lamp->spotsi) * la->spotblend;
|
|
lamp->k = la->k;
|
|
|
|
lamp->dist = la->dist;
|
|
lamp->falloff_type = la->falloff_type;
|
|
lamp->att1 = la->att1;
|
|
lamp->att2 = la->att2;
|
|
lamp->coeff_const = la->coeff_const;
|
|
lamp->coeff_lin = la->coeff_lin;
|
|
lamp->coeff_quad = la->coeff_quad;
|
|
lamp->curfalloff = la->curfalloff;
|
|
|
|
/* initshadowbuf */
|
|
lamp->bias = 0.02f * la->bias;
|
|
lamp->size = la->bufsize;
|
|
lamp->d = la->clipsta;
|
|
lamp->clipend = la->clipend;
|
|
|
|
/* arbitrary correction for the fact we do no soft transition */
|
|
lamp->bias *= 0.25f;
|
|
}
|
|
|
|
static void gpu_lamp_shadow_free(GPULamp *lamp)
|
|
{
|
|
if (lamp->tex) {
|
|
GPU_texture_free(lamp->tex);
|
|
lamp->tex = NULL;
|
|
}
|
|
if (lamp->depthtex) {
|
|
GPU_texture_free(lamp->depthtex);
|
|
lamp->depthtex = NULL;
|
|
}
|
|
if (lamp->fb) {
|
|
GPU_framebuffer_free(lamp->fb);
|
|
lamp->fb = NULL;
|
|
}
|
|
if (lamp->blurtex) {
|
|
GPU_texture_free(lamp->blurtex);
|
|
lamp->blurtex = NULL;
|
|
}
|
|
if (lamp->blurfb) {
|
|
GPU_framebuffer_free(lamp->blurfb);
|
|
lamp->blurfb = NULL;
|
|
}
|
|
}
|
|
|
|
static GPUTexture *gpu_lamp_create_vsm_shadow_map(int size)
|
|
{
|
|
return GPU_texture_create_2D_custom(size, size, 2, GPU_RG32F, NULL, NULL);
|
|
}
|
|
|
|
LampEngineData *GPU_lamp_engine_data_get(Scene *scene, Object *ob, Object *par, struct RenderEngineType *re)
|
|
{
|
|
GPULamp *lamp;
|
|
LinkData *link;
|
|
|
|
for (link = ob->gpulamp.first; link; link = link->next) {
|
|
lamp = (GPULamp *)link->data;
|
|
|
|
if ((lamp->par == par) && (lamp->scene == scene) && (lamp->re == re))
|
|
return &lamp->data;
|
|
}
|
|
|
|
lamp = MEM_callocN(sizeof(GPULamp), "GPULamp");
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPULampLink");
|
|
link->data = lamp;
|
|
BLI_addtail(&ob->gpulamp, link);
|
|
|
|
lamp->scene = scene;
|
|
lamp->ob = ob;
|
|
lamp->par = par;
|
|
lamp->la = ob->data;
|
|
lamp->re = re;
|
|
|
|
return &lamp->data;
|
|
}
|
|
|
|
GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par)
|
|
{
|
|
Lamp *la;
|
|
GPULamp *lamp;
|
|
LinkData *link;
|
|
|
|
for (link = ob->gpulamp.first; link; link = link->next) {
|
|
lamp = (GPULamp *)link->data;
|
|
|
|
if (lamp->par == par && lamp->scene == scene)
|
|
return link->data;
|
|
}
|
|
|
|
lamp = MEM_callocN(sizeof(GPULamp), "GPULamp");
|
|
|
|
link = MEM_callocN(sizeof(LinkData), "GPULampLink");
|
|
link->data = lamp;
|
|
BLI_addtail(&ob->gpulamp, link);
|
|
|
|
la = ob->data;
|
|
gpu_lamp_from_blender(scene, ob, par, la, lamp);
|
|
|
|
if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) ||
|
|
(la->type == LA_SUN && (la->mode & LA_SHAD_RAY)))
|
|
{
|
|
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
|
|
lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
|
|
lamp->tex = gpu_lamp_create_vsm_shadow_map(lamp->size);
|
|
lamp->blurtex = gpu_lamp_create_vsm_shadow_map(lamp->size * 0.5);
|
|
|
|
lamp->fb = GPU_framebuffer_create();
|
|
GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0);
|
|
GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0);
|
|
|
|
lamp->blurfb = GPU_framebuffer_create();
|
|
GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0);
|
|
|
|
if (!GPU_framebuffer_check_valid(lamp->fb, NULL) ||
|
|
!GPU_framebuffer_check_valid(lamp->blurfb, NULL))
|
|
{
|
|
gpu_lamp_shadow_free(lamp);
|
|
return lamp;
|
|
}
|
|
}
|
|
else {
|
|
lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
|
|
|
|
GPU_texture_bind(lamp->tex, 0);
|
|
GPU_texture_compare_mode(lamp->tex, true);
|
|
GPU_texture_filter_mode(lamp->tex, true);
|
|
GPU_texture_unbind(lamp->tex);
|
|
|
|
lamp->fb = GPU_framebuffer_create();
|
|
GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0);
|
|
|
|
if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
|
|
gpu_lamp_shadow_free(lamp);
|
|
return lamp;
|
|
}
|
|
}
|
|
|
|
GPU_framebuffer_restore();
|
|
|
|
lamp->shadow_color[0] = la->shdwr;
|
|
lamp->shadow_color[1] = la->shdwg;
|
|
lamp->shadow_color[2] = la->shdwb;
|
|
}
|
|
else {
|
|
lamp->shadow_color[0] = 1.0;
|
|
lamp->shadow_color[1] = 1.0;
|
|
lamp->shadow_color[2] = 1.0;
|
|
}
|
|
|
|
return lamp;
|
|
}
|
|
|
|
void GPU_lamp_engine_data_free(LampEngineData *led)
|
|
{
|
|
for (int i = 0; i < MAX_LAMP_DATA; ++i) {
|
|
if (led->storage[i]) {
|
|
MEM_freeN(led->storage[i]);
|
|
led->storage[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GPU_lamp_free(Object *ob)
|
|
{
|
|
GPULamp *lamp;
|
|
LinkData *link;
|
|
|
|
for (link = ob->gpulamp.first; link; link = link->next) {
|
|
lamp = link->data;
|
|
|
|
gpu_lamp_shadow_free(lamp);
|
|
GPU_lamp_engine_data_free(&lamp->data);
|
|
|
|
MEM_freeN(lamp);
|
|
}
|
|
|
|
BLI_freelistN(&ob->gpulamp);
|
|
}
|
|
|
|
bool GPU_lamp_has_shadow_buffer(GPULamp *lamp)
|
|
{
|
|
return (!(lamp->scene->gm.flag & GAME_GLSL_NO_SHADOWS) &&
|
|
!(lamp->scene->gm.flag & GAME_GLSL_NO_LIGHTS) &&
|
|
lamp->tex && lamp->fb);
|
|
}
|
|
|
|
void GPU_lamp_update_buffer_mats(GPULamp *lamp)
|
|
{
|
|
float rangemat[4][4], persmat[4][4];
|
|
|
|
/* initshadowbuf */
|
|
invert_m4_m4(lamp->viewmat, lamp->obmat);
|
|
normalize_v3(lamp->viewmat[0]);
|
|
normalize_v3(lamp->viewmat[1]);
|
|
normalize_v3(lamp->viewmat[2]);
|
|
|
|
/* makeshadowbuf */
|
|
mul_m4_m4m4(persmat, lamp->winmat, lamp->viewmat);
|
|
|
|
/* opengl depth buffer is range 0.0..1.0 instead of -1.0..1.0 in blender */
|
|
unit_m4(rangemat);
|
|
rangemat[0][0] = 0.5f;
|
|
rangemat[1][1] = 0.5f;
|
|
rangemat[2][2] = 0.5f;
|
|
rangemat[3][0] = 0.5f;
|
|
rangemat[3][1] = 0.5f;
|
|
rangemat[3][2] = 0.5f;
|
|
|
|
mul_m4_m4m4(lamp->persmat, rangemat, persmat);
|
|
}
|
|
|
|
void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsize, float winmat[4][4])
|
|
{
|
|
GPU_lamp_update_buffer_mats(lamp);
|
|
|
|
/* opengl */
|
|
glDisable(GL_SCISSOR_TEST);
|
|
GPU_framebuffer_bind(lamp->fb);
|
|
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE)
|
|
GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE));
|
|
|
|
/* set matrices */
|
|
copy_m4_m4(viewmat, lamp->viewmat);
|
|
copy_m4_m4(winmat, lamp->winmat);
|
|
*winsize = lamp->size;
|
|
}
|
|
|
|
static void gpu_lamp_shadow_blur(GPULamp *lamp)
|
|
{
|
|
const float scaleh[2] = {1.0f / GPU_texture_width(lamp->blurtex), 0.0f};
|
|
const float scalev[2] = {0.0f, 1.0f / GPU_texture_height(lamp->tex)};
|
|
|
|
GPUShader *blur_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SEP_GAUSSIAN_BLUR);
|
|
|
|
if (!blur_shader)
|
|
return;
|
|
|
|
int tex_loc = GPU_shader_get_uniform(blur_shader, "textureSource");
|
|
int scale_loc = GPU_shader_get_uniform(blur_shader, "ScaleU");
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
GPU_shader_bind(blur_shader);
|
|
|
|
/* Blurring horizontally */
|
|
GPU_framebuffer_bind(lamp->blurfb);
|
|
GPU_texture_bind(lamp->tex, 0);
|
|
GPU_shader_uniform_vector(blur_shader, scale_loc, 2, 1, scaleh);
|
|
GPU_shader_uniform_texture(blur_shader, tex_loc, lamp->tex);
|
|
GWN_draw_primitive(GL_TRIANGLES, 3);
|
|
|
|
/* Blurring vertically */
|
|
GPU_framebuffer_bind(lamp->fb);
|
|
GPU_texture_bind(lamp->blurtex, 0);
|
|
GPU_shader_uniform_vector(blur_shader, scale_loc, 2, 1, scalev);
|
|
GPU_shader_uniform_texture(blur_shader, tex_loc, lamp->blurtex);
|
|
GWN_draw_primitive(GL_TRIANGLES, 3);
|
|
}
|
|
|
|
void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp)
|
|
{
|
|
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
|
|
GPU_shader_unbind();
|
|
gpu_lamp_shadow_blur(lamp);
|
|
}
|
|
|
|
GPU_framebuffer_restore();
|
|
glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
int GPU_lamp_shadow_buffer_type(GPULamp *lamp)
|
|
{
|
|
return lamp->la->shadowmap_type;
|
|
}
|
|
|
|
int GPU_lamp_shadow_bind_code(GPULamp *lamp)
|
|
{
|
|
return lamp->tex ? GPU_texture_opengl_bindcode(lamp->tex) : -1;
|
|
}
|
|
|
|
float *GPU_lamp_dynpersmat(GPULamp *lamp)
|
|
{
|
|
return &lamp->dynpersmat[0][0];
|
|
}
|
|
|
|
int GPU_lamp_shadow_layer(GPULamp *lamp)
|
|
{
|
|
if (lamp->fb && lamp->tex && (lamp->mode & (LA_LAYER | LA_LAYER_SHADOW)))
|
|
return lamp->lay;
|
|
else
|
|
return -1;
|
|
}
|