Previously, every RenderPass would have a bitfield that specified its type. That limits the number of passes to 32, which was reached a while ago. However, most of the code already supported arbitrary RenderPasses since they were also used to store Multilayer EXR images. Therefore, this commit completely removes the passflag from RenderPass and changes all code to use the unique pass name for identification. Since Blender Internal relies on hardcoded passes and to preserve compatibility, 32 pass names are reserved for the old hardcoded passes. To support these arbitrary passes, the Render Result compositor node now adds dynamic sockets. For compatibility, the old hardcoded sockets are always stored and just hidden when the corresponding pass isn't available. To use these changes, the Render Engine API now includes a function that allows render engines to add arbitrary passes to the render result. To be able to add options for these passes, addons can now add their own properties to SceneRenderLayers. To keep the compositor input node updated, render engine plugins have to implement a callback that registers all the passes that will be generated. From a user perspective, nothing should change with this commit. Differential Revision: https://developer.blender.org/D2443 Differential Revision: https://developer.blender.org/D2444
2029 lines
50 KiB
C
2029 lines
50 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) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* Contributors: Hos, Robert Wenzlaff.
|
|
* Contributors: 2004/2005/2006 Blender Foundation, full recode
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/render/intern/source/rendercore.c
|
|
* \ingroup render
|
|
*/
|
|
|
|
|
|
/* system includes */
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
/* External modules: */
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_threads.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_group_types.h"
|
|
|
|
/* local include */
|
|
#include "renderpipeline.h"
|
|
#include "render_result.h"
|
|
#include "render_types.h"
|
|
#include "renderdatabase.h"
|
|
#include "occlusion.h"
|
|
#include "pixelblending.h"
|
|
#include "pixelshading.h"
|
|
#include "shadbuf.h"
|
|
#include "shading.h"
|
|
#include "sss.h"
|
|
#include "zbuf.h"
|
|
|
|
/* own include */
|
|
#include "rendercore.h"
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
|
/* only to be used here in this file, it's for speed */
|
|
extern struct Render R;
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
/* x and y are current pixels in rect to be rendered */
|
|
/* do not normalize! */
|
|
void calc_view_vector(float view[3], float x, float y)
|
|
{
|
|
|
|
view[2]= -ABS(R.clipsta);
|
|
|
|
if (R.r.mode & R_ORTHO) {
|
|
view[0]= view[1]= 0.0f;
|
|
}
|
|
else {
|
|
|
|
if (R.r.mode & R_PANORAMA) {
|
|
x-= R.panodxp;
|
|
}
|
|
|
|
/* move x and y to real viewplane coords */
|
|
x = (x / (float)R.winx);
|
|
view[0] = R.viewplane.xmin + x * BLI_rctf_size_x(&R.viewplane);
|
|
|
|
y = (y / (float)R.winy);
|
|
view[1] = R.viewplane.ymin + y * BLI_rctf_size_y(&R.viewplane);
|
|
|
|
// if (R.flag & R_SEC_FIELD) {
|
|
// if (R.r.mode & R_ODDFIELD) view[1]= (y+R.ystart)*R.ycor;
|
|
// else view[1]= (y+R.ystart+1.0)*R.ycor;
|
|
// }
|
|
// else view[1]= (y+R.ystart+R.bluroffsy+0.5)*R.ycor;
|
|
|
|
if (R.r.mode & R_PANORAMA) {
|
|
float u= view[0] + R.panodxv; float v= view[2];
|
|
view[0]= R.panoco*u + R.panosi*v;
|
|
view[2]= -R.panosi*u + R.panoco*v;
|
|
}
|
|
}
|
|
}
|
|
|
|
void calc_renderco_ortho(float co[3], float x, float y, int z)
|
|
{
|
|
/* x and y 3d coordinate can be derived from pixel coord and winmat */
|
|
float fx= 2.0f/(R.winx*R.winmat[0][0]);
|
|
float fy= 2.0f/(R.winy*R.winmat[1][1]);
|
|
float zco;
|
|
|
|
co[0]= (x - 0.5f*R.winx)*fx - R.winmat[3][0]/R.winmat[0][0];
|
|
co[1]= (y - 0.5f*R.winy)*fy - R.winmat[3][1]/R.winmat[1][1];
|
|
|
|
zco= ((float)z)/2147483647.0f;
|
|
co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
|
|
}
|
|
|
|
void calc_renderco_zbuf(float co[3], const float view[3], int z)
|
|
{
|
|
float fac, zco;
|
|
|
|
/* inverse of zbuf calc: zbuf = MAXZ*hoco_z/hoco_w */
|
|
zco= ((float)z)/2147483647.0f;
|
|
co[2]= R.winmat[3][2]/( R.winmat[2][3]*zco - R.winmat[2][2] );
|
|
|
|
fac= co[2]/view[2];
|
|
co[0]= fac*view[0];
|
|
co[1]= fac*view[1];
|
|
}
|
|
|
|
/* also used in zbuf.c and shadbuf.c */
|
|
int count_mask(unsigned short mask)
|
|
{
|
|
if (R.samples)
|
|
return (R.samples->cmask[mask & 255]+R.samples->cmask[mask>>8]);
|
|
return 0;
|
|
}
|
|
|
|
static int calchalo_z(HaloRen *har, int zz)
|
|
{
|
|
|
|
if (har->type & HA_ONLYSKY) {
|
|
if (zz < 0x7FFFFFF0) zz= - 0x7FFFFF; /* edge render messes zvalues */
|
|
}
|
|
else {
|
|
zz= (zz>>8);
|
|
}
|
|
return zz;
|
|
}
|
|
|
|
|
|
|
|
static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, int od, float dist, float xn, float yn, PixStr *ps)
|
|
{
|
|
float col[4], accol[4], fac;
|
|
int amount, amountm, zz, flarec, sample, fullsample, mask=0;
|
|
|
|
fullsample= (totsample > 1);
|
|
amount= 0;
|
|
accol[0] = accol[1] = accol[2] = accol[3]= 0.0f;
|
|
col[0] = col[1] = col[2] = col[3]= 0.0f;
|
|
flarec= har->flarec;
|
|
|
|
while (ps) {
|
|
amountm= count_mask(ps->mask);
|
|
amount+= amountm;
|
|
|
|
zz= calchalo_z(har, ps->z);
|
|
if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
|
|
if (shadeHaloFloat(har, col, zz, dist, xn, yn, flarec)) {
|
|
flarec= 0;
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++)
|
|
if (ps->mask & (1 << sample)) {
|
|
float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
addalphaAddfacFloat(pass + od*4, col, har->add);
|
|
}
|
|
}
|
|
else {
|
|
fac= ((float)amountm)/(float)R.osa;
|
|
accol[0]+= fac*col[0];
|
|
accol[1]+= fac*col[1];
|
|
accol[2]+= fac*col[2];
|
|
accol[3]+= fac*col[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
mask |= ps->mask;
|
|
ps= ps->next;
|
|
}
|
|
|
|
/* now do the sky sub-pixels */
|
|
amount= R.osa-amount;
|
|
if (amount) {
|
|
if (shadeHaloFloat(har, col, 0x7FFFFF, dist, xn, yn, flarec)) {
|
|
if (!fullsample) {
|
|
fac= ((float)amount)/(float)R.osa;
|
|
accol[0]+= fac*col[0];
|
|
accol[1]+= fac*col[1];
|
|
accol[2]+= fac*col[2];
|
|
accol[3]+= fac*col[3];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++)
|
|
if (!(mask & (1 << sample))) {
|
|
float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
addalphaAddfacFloat(pass + od*4, col, har->add);
|
|
}
|
|
}
|
|
else {
|
|
col[0]= accol[0];
|
|
col[1]= accol[1];
|
|
col[2]= accol[2];
|
|
col[3]= accol[3];
|
|
|
|
for (sample=0; sample<totsample; sample++) {
|
|
float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
addalphaAddfacFloat(pass + od*4, col, har->add);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void halo_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
HaloRen *har;
|
|
rcti disprect= pa->disprect, testrect= pa->disprect;
|
|
float dist, xsq, ysq, xn, yn;
|
|
float col[4];
|
|
intptr_t *rd= NULL;
|
|
int a, *rz, zz, y, sample, totsample, od;
|
|
short minx, maxx, miny, maxy, x;
|
|
unsigned int lay= rl->lay;
|
|
|
|
/* we don't render halos in the cropped area, gives errors in flare counter */
|
|
if (pa->crop) {
|
|
testrect.xmin+= pa->crop;
|
|
testrect.xmax-= pa->crop;
|
|
testrect.ymin+= pa->crop;
|
|
testrect.ymax-= pa->crop;
|
|
}
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (a=0; a<R.tothalo; a++) {
|
|
har= R.sortedhalos[a];
|
|
|
|
/* layer test, clip halo with y */
|
|
if ((har->lay & lay) == 0) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.ymin > har->maxy) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.ymax < har->miny) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
|
|
minx= floor(har->xs-har->rad);
|
|
maxx= ceil(har->xs+har->rad);
|
|
|
|
if (testrect.xmin > maxx) {
|
|
/* pass */
|
|
}
|
|
else if (testrect.xmax < minx) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
|
|
minx = max_ii(minx, testrect.xmin);
|
|
maxx = min_ii(maxx, testrect.xmax);
|
|
|
|
miny = max_ii(har->miny, testrect.ymin);
|
|
maxy = min_ii(har->maxy, testrect.ymax);
|
|
|
|
for (y=miny; y<maxy; y++) {
|
|
int rectofs= (y-disprect.ymin)*pa->rectx + (minx - disprect.xmin);
|
|
rz= pa->rectz + rectofs;
|
|
od= rectofs;
|
|
|
|
if (pa->rectdaps)
|
|
rd= pa->rectdaps + rectofs;
|
|
|
|
yn= (y-har->ys)*R.ycor;
|
|
ysq= yn*yn;
|
|
|
|
for (x=minx; x<maxx; x++, rz++, od++) {
|
|
xn= x- har->xs;
|
|
xsq= xn*xn;
|
|
dist= xsq+ysq;
|
|
if (dist<har->radsq) {
|
|
if (rd && *rd) {
|
|
halo_pixelstruct(har, rlpp, totsample, od, dist, xn, yn, (PixStr *)*rd);
|
|
}
|
|
else {
|
|
zz= calchalo_z(har, *rz);
|
|
if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
|
|
if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) {
|
|
for (sample=0; sample<totsample; sample++) {
|
|
float * rect= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
addalphaAddfacFloat(rect + od*4, col, har->add);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (rd) rd++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (R.test_break(R.tbh) ) break;
|
|
}
|
|
}
|
|
|
|
static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
ShadeInput shi;
|
|
float *pass;
|
|
float fac, col[4];
|
|
intptr_t *rd= pa->rectdaps;
|
|
const int *rz= pa->rectz;
|
|
int x, y, sample, totsample, fullsample, od;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
fullsample= (totsample > 1);
|
|
|
|
shade_input_initialize(&shi, pa, rl, 0); /* this zero's ShadeInput for us */
|
|
|
|
for (od=0, y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, rz++, od++) {
|
|
|
|
calc_view_vector(shi.view, x, y);
|
|
|
|
if (rd && *rd) {
|
|
PixStr *ps= (PixStr *)*rd;
|
|
int count, totsamp= 0, mask= 0;
|
|
|
|
while (ps) {
|
|
if (R.r.mode & R_ORTHO)
|
|
calc_renderco_ortho(shi.co, (float)x, (float)y, ps->z);
|
|
else
|
|
calc_renderco_zbuf(shi.co, shi.view, ps->z);
|
|
|
|
totsamp+= count= count_mask(ps->mask);
|
|
mask |= ps->mask;
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++) {
|
|
if (ps->mask & (1 << sample)) {
|
|
pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od * 4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fac= ((float)count)/(float)R.osa;
|
|
pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od * 4;
|
|
pass[0]+= fac*col[0];
|
|
pass[1]+= fac*col[1];
|
|
pass[2]+= fac*col[2];
|
|
pass[3]+= fac*col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
|
|
ps= ps->next;
|
|
}
|
|
|
|
if (totsamp<R.osa) {
|
|
shi.co[2]= 0.0f;
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
if (fullsample) {
|
|
for (sample=0; sample<totsample; sample++) {
|
|
if (!(mask & (1 << sample))) {
|
|
|
|
pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od * 4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fac= ((float)R.osa-totsamp)/(float)R.osa;
|
|
pass = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od * 4;
|
|
pass[0]+= fac*col[0];
|
|
pass[1]+= fac*col[1];
|
|
pass[2]+= fac*col[2];
|
|
pass[3]+= fac*col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (R.r.mode & R_ORTHO)
|
|
calc_renderco_ortho(shi.co, (float)x, (float)y, *rz);
|
|
else
|
|
calc_renderco_zbuf(shi.co, shi.view, *rz);
|
|
|
|
col[0]= col[1]= col[2]= col[3]= 0.0f;
|
|
renderspothalo(&shi, col, 1.0f);
|
|
|
|
for (sample=0; sample<totsample; sample++) {
|
|
pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od * 4;
|
|
pass[0]+= col[0];
|
|
pass[1]+= col[1];
|
|
pass[2]+= col[2];
|
|
pass[3]+= col[3];
|
|
if (pass[3]>1.0f) pass[3]= 1.0f;
|
|
}
|
|
}
|
|
|
|
if (rd) rd++;
|
|
}
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
|
|
|
|
/* ********************* MAINLOOPS ******************** */
|
|
|
|
/* osa version */
|
|
static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, ShadeInput *shi, ShadeResult *shr)
|
|
{
|
|
RenderPass *rpass;
|
|
|
|
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
|
|
float *fp, *col= NULL;
|
|
int pixsize= 3;
|
|
|
|
if(STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
|
|
add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx);
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_Z)) {
|
|
fp = rpass->rect + offset;
|
|
*fp = shr->z;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_RGBA)) {
|
|
col = shr->col;
|
|
pixsize = 4;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_EMIT)) {
|
|
col = shr->emit;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) {
|
|
col = shr->diff;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_SPEC)) {
|
|
col = shr->spec;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_SHADOW)) {
|
|
col = shr->shad;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_AO)) {
|
|
col = shr->ao;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) {
|
|
col = shr->env;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_INDIRECT)) {
|
|
col = shr->indirect;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_REFLECT)) {
|
|
col = shr->refl;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_REFRACT)) {
|
|
col = shr->refr;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_NORMAL)) {
|
|
col = shr->nor;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_UV)) {
|
|
/* box filter only, gauss will screwup UV too much */
|
|
if (shi->totuv) {
|
|
float mult = (float)count_mask(curmask)/(float)R.osa;
|
|
fp = rpass->rect + 3*offset;
|
|
fp[0]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[0]);
|
|
fp[1]+= mult*(0.5f + 0.5f*shi->uv[shi->actuv].uv[1]);
|
|
fp[2]+= mult;
|
|
}
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_INDEXOB)) {
|
|
/* no filter */
|
|
if (shi->vlr) {
|
|
fp = rpass->rect + offset;
|
|
if (*fp==0.0f)
|
|
*fp = (float)shi->obr->ob->index;
|
|
}
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_INDEXMA)) {
|
|
/* no filter */
|
|
if (shi->vlr) {
|
|
fp = rpass->rect + offset;
|
|
if (*fp==0.0f)
|
|
*fp = (float)shi->mat->index;
|
|
}
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_MIST)) {
|
|
/* */
|
|
col = &shr->mist;
|
|
pixsize = 1;
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_VECTOR)) {
|
|
/* add minimum speed in pixel, no filter */
|
|
fp = rpass->rect + 4*offset;
|
|
if ( (ABS(shr->winspeed[0]) + ABS(shr->winspeed[1]))< (ABS(fp[0]) + ABS(fp[1])) ) {
|
|
fp[0] = shr->winspeed[0];
|
|
fp[1] = shr->winspeed[1];
|
|
}
|
|
if ( (ABS(shr->winspeed[2]) + ABS(shr->winspeed[3]))< (ABS(fp[2]) + ABS(fp[3])) ) {
|
|
fp[2] = shr->winspeed[2];
|
|
fp[3] = shr->winspeed[3];
|
|
}
|
|
}
|
|
else if(STREQ(rpass->name, RE_PASSNAME_RAYHITS)) {
|
|
/* */
|
|
col = shr->rayhits;
|
|
pixsize= 4;
|
|
}
|
|
|
|
if (col) {
|
|
fp= rpass->rect + pixsize*offset;
|
|
add_filt_fmask_pixsize(curmask, col, fp, rectx, pixsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* non-osa version */
|
|
static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult *shr)
|
|
{
|
|
RenderPass *rpass;
|
|
float *fp;
|
|
|
|
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
|
|
float *col= NULL, uvcol[3];
|
|
int a, pixsize= 3;
|
|
|
|
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
|
|
/* copy combined to use for preview */
|
|
copy_v4_v4(rpass->rect + 4*offset, shr->combined);
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_Z)) {
|
|
fp = rpass->rect + offset;
|
|
*fp = shr->z;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_RGBA)) {
|
|
col = shr->col;
|
|
pixsize = 4;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_EMIT)) {
|
|
col = shr->emit;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_DIFFUSE)) {
|
|
col = shr->diff;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_SPEC)) {
|
|
col = shr->spec;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_SHADOW)) {
|
|
col = shr->shad;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_AO)) {
|
|
col = shr->ao;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_ENVIRONMENT)) {
|
|
col = shr->env;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_INDIRECT)) {
|
|
col = shr->indirect;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_REFLECT)) {
|
|
col = shr->refl;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_REFRACT)) {
|
|
col = shr->refr;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_NORMAL)) {
|
|
col = shr->nor;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_UV)) {
|
|
if (shi->totuv) {
|
|
uvcol[0] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[0];
|
|
uvcol[1] = 0.5f + 0.5f*shi->uv[shi->actuv].uv[1];
|
|
uvcol[2] = 1.0f;
|
|
col = uvcol;
|
|
}
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_VECTOR)) {
|
|
col = shr->winspeed;
|
|
pixsize = 4;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_INDEXOB)) {
|
|
if (shi->vlr) {
|
|
fp = rpass->rect + offset;
|
|
*fp = (float)shi->obr->ob->index;
|
|
}
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_INDEXMA)) {
|
|
if (shi->vlr) {
|
|
fp = rpass->rect + offset;
|
|
*fp = (float)shi->mat->index;
|
|
}
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_MIST)) {
|
|
fp = rpass->rect + offset;
|
|
*fp = shr->mist;
|
|
}
|
|
else if (STREQ(rpass->name, RE_PASSNAME_RAYHITS)) {
|
|
col = shr->rayhits;
|
|
pixsize = 4;
|
|
}
|
|
|
|
if (col) {
|
|
fp = rpass->rect + pixsize*offset;
|
|
for (a=0; a<pixsize; a++)
|
|
fp[a] = col[a];
|
|
}
|
|
}
|
|
}
|
|
|
|
int get_sample_layers(RenderPart *pa, RenderLayer *rl, RenderLayer **rlpp)
|
|
{
|
|
|
|
if (pa->fullresult.first) {
|
|
int sample, nr= BLI_findindex(&pa->result->layers, rl);
|
|
|
|
for (sample=0; sample<R.osa; sample++) {
|
|
RenderResult *rr= BLI_findlink(&pa->fullresult, sample);
|
|
|
|
rlpp[sample]= BLI_findlink(&rr->layers, nr);
|
|
}
|
|
return R.osa;
|
|
}
|
|
else {
|
|
rlpp[0]= rl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* only do sky, is default in the solid layer (shade_tile) btw */
|
|
static void sky_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int x, y, od=0, totsample;
|
|
|
|
if (R.r.alphamode!=R_ADDSKY)
|
|
return;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od+=4) {
|
|
float col[4];
|
|
int sample;
|
|
bool done = false;
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
float *pass = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
pass += od;
|
|
|
|
if (pass[3]<1.0f) {
|
|
|
|
if (done==0) {
|
|
shadeSkyPixel(col, x, y, pa->thread);
|
|
done = true;
|
|
}
|
|
|
|
if (pass[3]==0.0f) {
|
|
copy_v4_v4(pass, col);
|
|
pass[3] = 1.0f;
|
|
}
|
|
else {
|
|
addAlphaUnderFloat(pass, col);
|
|
pass[3] = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
|
|
static void atm_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderPass *zpass;
|
|
GroupObject *go;
|
|
LampRen *lar;
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int totsample;
|
|
int x, y, od= 0;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
/* check that z pass is enabled */
|
|
if (pa->rectz==NULL) return;
|
|
for (zpass= rl->passes.first; zpass; zpass= zpass->next)
|
|
if (STREQ(zpass->name, RE_PASSNAME_Z))
|
|
break;
|
|
|
|
if (zpass==NULL) return;
|
|
|
|
/* check for at least one sun lamp that its atmosphere flag is enabled */
|
|
for (go=R.lights.first; go; go= go->next) {
|
|
lar= go->lampren;
|
|
if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_AP))
|
|
break;
|
|
}
|
|
/* do nothign and return if there is no sun lamp */
|
|
if (go==NULL)
|
|
return;
|
|
|
|
/* for each x,y and each sample, and each sun lamp*/
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, od++) {
|
|
int sample;
|
|
|
|
for (sample=0; sample<totsample; sample++) {
|
|
const float *zrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_Z, R.viewname) + od;
|
|
float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname) + 4*od;
|
|
float rgb[3] = {0};
|
|
bool done = false;
|
|
|
|
for (go=R.lights.first; go; go= go->next) {
|
|
|
|
|
|
lar= go->lampren;
|
|
if (lar->type==LA_SUN && lar->sunsky) {
|
|
|
|
/* if it's sky continue and don't apply atmosphere effect on it */
|
|
if (*zrect >= 9.9e10f || rgbrect[3]==0.0f) {
|
|
continue;
|
|
}
|
|
|
|
if ((lar->sunsky->effect_type & LA_SUN_EFFECT_AP)) {
|
|
float tmp_rgb[3];
|
|
|
|
/* skip if worldspace lamp vector is below horizon */
|
|
if (go->ob->obmat[2][2] < 0.f) {
|
|
continue;
|
|
}
|
|
|
|
copy_v3_v3(tmp_rgb, rgbrect);
|
|
if (rgbrect[3]!=1.0f) { /* de-premul */
|
|
mul_v3_fl(tmp_rgb, 1.0f/rgbrect[3]);
|
|
}
|
|
shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect);
|
|
if (rgbrect[3]!=1.0f) { /* premul */
|
|
mul_v3_fl(tmp_rgb, rgbrect[3]);
|
|
}
|
|
|
|
if (done==0) {
|
|
copy_v3_v3(rgb, tmp_rgb);
|
|
done = true;
|
|
}
|
|
else {
|
|
rgb[0] = 0.5f*rgb[0] + 0.5f*tmp_rgb[0];
|
|
rgb[1] = 0.5f*rgb[1] + 0.5f*tmp_rgb[1];
|
|
rgb[2] = 0.5f*rgb[2] + 0.5f*tmp_rgb[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if at least for one sun lamp aerial perspective was applied*/
|
|
if (done) {
|
|
copy_v3_v3(rgbrect, rgb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void shadeDA_tile(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderResult *rr= pa->result;
|
|
ShadeSample ssamp;
|
|
intptr_t *rd, *rectdaps= pa->rectdaps;
|
|
int samp;
|
|
int x, y, seed, crop=0, offs=0, od;
|
|
|
|
if (R.test_break(R.tbh)) return;
|
|
|
|
/* irregular shadowb buffer creation */
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
|
|
/* we set per pixel a fixed seed, for random AO and shadow samples */
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
|
|
/* general shader info, passes */
|
|
shade_sample_initialize(&ssamp, pa, rl);
|
|
|
|
/* occlusion caching */
|
|
if (R.occlusiontree)
|
|
cache_occ_samples(&R, pa, &ssamp);
|
|
|
|
/* filtered render, for now we assume only 1 filter size */
|
|
if (pa->crop) {
|
|
crop= 1;
|
|
rectdaps+= pa->rectx + 1;
|
|
offs= pa->rectx + 1;
|
|
}
|
|
|
|
/* scanline updates have to be 2 lines behind */
|
|
rr->renrect.ymin = 0;
|
|
rr->renrect.ymax = -2*crop;
|
|
rr->renlay= rl;
|
|
|
|
for (y=pa->disprect.ymin+crop; y<pa->disprect.ymax-crop; y++, rr->renrect.ymax++) {
|
|
rd= rectdaps;
|
|
od= offs;
|
|
|
|
for (x=pa->disprect.xmin+crop; x<pa->disprect.xmax-crop; x++, rd++, od++) {
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
if (*rd) {
|
|
if (shade_samples(&ssamp, (PixStr *)(*rd), x, y)) {
|
|
|
|
/* multisample buffers or filtered mask filling? */
|
|
if (pa->fullresult.first) {
|
|
int a;
|
|
for (samp=0; samp<ssamp.tot; samp++) {
|
|
int smask= ssamp.shi[samp].mask;
|
|
for (a=0; a<R.osa; a++) {
|
|
int mask= 1<<a;
|
|
if (smask & mask)
|
|
add_passes(ssamp.rlpp[a], od, &ssamp.shi[samp], &ssamp.shr[samp]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (samp=0; samp<ssamp.tot; samp++)
|
|
add_filt_passes(rl, ssamp.shi[samp].mask, pa->rectx, od, &ssamp.shi[samp], &ssamp.shr[samp]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rectdaps+= pa->rectx;
|
|
offs+= pa->rectx;
|
|
|
|
if (y&1) if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
/* disable scanline updating */
|
|
rr->renlay= NULL;
|
|
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
|
|
if (R.occlusiontree)
|
|
free_occ_samples(&R, pa);
|
|
}
|
|
|
|
/* ************* pixel struct ******** */
|
|
|
|
|
|
static PixStrMain *addpsmain(ListBase *lb)
|
|
{
|
|
PixStrMain *psm;
|
|
|
|
psm= (PixStrMain *)MEM_mallocN(sizeof(PixStrMain), "pixstrMain");
|
|
BLI_addtail(lb, psm);
|
|
|
|
psm->ps= (PixStr *)MEM_mallocN(4096*sizeof(PixStr), "pixstr");
|
|
psm->counter= 0;
|
|
|
|
return psm;
|
|
}
|
|
|
|
static void freeps(ListBase *lb)
|
|
{
|
|
PixStrMain *psm, *psmnext;
|
|
|
|
for (psm= lb->first; psm; psm= psmnext) {
|
|
psmnext= psm->next;
|
|
if (psm->ps)
|
|
MEM_freeN(psm->ps);
|
|
MEM_freeN(psm);
|
|
}
|
|
BLI_listbase_clear(lb);
|
|
}
|
|
|
|
static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int maskz, unsigned short mask)
|
|
{
|
|
PixStrMain *psm;
|
|
PixStr *ps, *last= NULL;
|
|
|
|
if (*rd) {
|
|
ps= (PixStr *)(*rd);
|
|
|
|
while (ps) {
|
|
if ( ps->obi == obi && ps->facenr == facenr ) {
|
|
ps->mask |= mask;
|
|
return;
|
|
}
|
|
last= ps;
|
|
ps= ps->next;
|
|
}
|
|
}
|
|
|
|
/* make new PS (pixel struct) */
|
|
psm= lb->last;
|
|
|
|
if (psm->counter==4095)
|
|
psm= addpsmain(lb);
|
|
|
|
ps= psm->ps + psm->counter++;
|
|
|
|
if (last) last->next= ps;
|
|
else *rd= (intptr_t)ps;
|
|
|
|
ps->next= NULL;
|
|
ps->obi= obi;
|
|
ps->facenr= facenr;
|
|
ps->z= z;
|
|
ps->maskz= maskz;
|
|
ps->mask = mask;
|
|
ps->shadfac= 0;
|
|
}
|
|
|
|
static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect)
|
|
{
|
|
float addcol[4];
|
|
int pix;
|
|
|
|
if (arect==NULL)
|
|
return;
|
|
|
|
for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) {
|
|
if (*arect != 0.0f) {
|
|
addcol[0]= *arect * R.r.edgeR;
|
|
addcol[1]= *arect * R.r.edgeG;
|
|
addcol[2]= *arect * R.r.edgeB;
|
|
addcol[3]= *arect;
|
|
addAlphaOverFloat(rectf, addcol);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */
|
|
static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
int y, sample, totsample;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
/* not for full sample, there we clamp after compositing */
|
|
if (totsample > 1)
|
|
return;
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
float *rectf = RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_COMBINED, R.viewname);
|
|
|
|
for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) {
|
|
rectf[0] = MAX2(rectf[0], 0.0f);
|
|
rectf[1] = MAX2(rectf[1], 0.0f);
|
|
rectf[2] = MAX2(rectf[2], 0.0f);
|
|
CLAMP(rectf[3], 0.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* adds only alpha values */
|
|
static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz)
|
|
{
|
|
/* use zbuffer to define edges, add it to the image */
|
|
int y, x, col, *rz, *rz1, *rz2, *rz3;
|
|
int zval1, zval2, zval3;
|
|
float *rf;
|
|
|
|
/* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */
|
|
rz= rectz;
|
|
if (rz==NULL) return;
|
|
|
|
for (y=0; y<pa->recty; y++)
|
|
for (x=0; x<pa->rectx; x++, rz++) (*rz)>>= 4;
|
|
|
|
rz1= rectz;
|
|
rz2= rz1+pa->rectx;
|
|
rz3= rz2+pa->rectx;
|
|
|
|
rf= rectf+pa->rectx+1;
|
|
|
|
for (y=0; y<pa->recty-2; y++) {
|
|
for (x=0; x<pa->rectx-2; x++, rz1++, rz2++, rz3++, rf++) {
|
|
|
|
/* prevent overflow with sky z values */
|
|
zval1= rz1[0] + 2*rz1[1] + rz1[2];
|
|
zval2= 2*rz2[0] + 2*rz2[2];
|
|
zval3= rz3[0] + 2*rz3[1] + rz3[2];
|
|
|
|
col= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 );
|
|
if (col<0) col= -col;
|
|
|
|
col >>= 5;
|
|
if (col > (1<<16)) col= (1<<16);
|
|
else col= (R.r.edgeint*col)>>8;
|
|
|
|
if (col>0) {
|
|
float fcol;
|
|
|
|
if (col>255) fcol= 1.0f;
|
|
else fcol= (float)col/255.0f;
|
|
|
|
if (R.osa)
|
|
*rf+= fcol/(float)R.osa;
|
|
else
|
|
*rf= fcol;
|
|
}
|
|
}
|
|
rz1+= 2;
|
|
rz2+= 2;
|
|
rz3+= 2;
|
|
rf+= 2;
|
|
}
|
|
|
|
/* shift back zbuf values, we might need it still */
|
|
rz= rectz;
|
|
for (y=0; y<pa->recty; y++)
|
|
for (x=0; x<pa->rectx; x++, rz++) (*rz)<<= 4;
|
|
|
|
}
|
|
|
|
static void reset_sky_speed(RenderPart *pa, RenderLayer *rl)
|
|
{
|
|
/* for all pixels with max speed, set to zero */
|
|
RenderLayer *rlpp[RE_MAX_OSA];
|
|
float *fp;
|
|
int a, sample, totsample;
|
|
|
|
totsample= get_sample_layers(pa, rl, rlpp);
|
|
|
|
for (sample= 0; sample<totsample; sample++) {
|
|
fp= RE_RenderLayerGetPass(rlpp[sample], RE_PASSNAME_VECTOR, R.viewname);
|
|
if (fp==NULL) break;
|
|
|
|
for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--)
|
|
if (fp[a] == PASS_VECTOR_MAX) fp[a]= 0.0f;
|
|
}
|
|
}
|
|
|
|
static unsigned short *make_solid_mask(RenderPart *pa)
|
|
{
|
|
intptr_t *rd= pa->rectdaps;
|
|
unsigned short *solidmask, *sp;
|
|
int x;
|
|
|
|
if (rd==NULL) return NULL;
|
|
|
|
sp=solidmask= MEM_mallocN(sizeof(short)*pa->rectx*pa->recty, "solidmask");
|
|
|
|
for (x=pa->rectx*pa->recty; x>0; x--, rd++, sp++) {
|
|
if (*rd) {
|
|
PixStr *ps= (PixStr *)*rd;
|
|
|
|
*sp= ps->mask;
|
|
for (ps= ps->next; ps; ps= ps->next)
|
|
*sp |= ps->mask;
|
|
}
|
|
else
|
|
*sp= 0;
|
|
}
|
|
|
|
return solidmask;
|
|
}
|
|
|
|
static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dmask, unsigned short smask)
|
|
{
|
|
unsigned short shared= dmask & smask;
|
|
float mul= 1.0f - source[3];
|
|
|
|
if (shared) { /* overlapping masks */
|
|
|
|
/* masks differ, we make a mixture of 'add' and 'over' */
|
|
if (shared!=dmask) {
|
|
float shared_bits= (float)count_mask(shared); /* alpha over */
|
|
float tot_bits= (float)count_mask(smask|dmask); /* alpha add */
|
|
|
|
float add= (tot_bits - shared_bits)/tot_bits; /* add level */
|
|
mul= add + (1.0f-add)*mul;
|
|
}
|
|
}
|
|
else if (dmask && smask) {
|
|
/* works for premul only, of course */
|
|
dest[0]+= source[0];
|
|
dest[1]+= source[1];
|
|
dest[2]+= source[2];
|
|
dest[3]+= source[3];
|
|
|
|
return;
|
|
}
|
|
|
|
dest[0]= (mul*dest[0]) + source[0];
|
|
dest[1]= (mul*dest[1]) + source[1];
|
|
dest[2]= (mul*dest[2]) + source[2];
|
|
dest[3]= (mul*dest[3]) + source[3];
|
|
}
|
|
|
|
typedef struct ZbufSolidData {
|
|
RenderLayer *rl;
|
|
ListBase *psmlist;
|
|
float *edgerect;
|
|
} ZbufSolidData;
|
|
|
|
static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data)
|
|
{
|
|
ZbufSolidData *sdata = (ZbufSolidData *)data;
|
|
ListBase *lb= sdata->psmlist;
|
|
intptr_t *rd= pa->rectdaps;
|
|
const int *ro= zspan->recto;
|
|
const int *rp= zspan->rectp;
|
|
const int *rz= zspan->rectz;
|
|
const int *rm= zspan->rectmask;
|
|
int x, y;
|
|
int mask= 1<<sample;
|
|
|
|
for (y=0; y<pa->recty; y++) {
|
|
for (x=0; x<pa->rectx; x++, rd++, rp++, ro++, rz++, rm++) {
|
|
if (*rp) {
|
|
addps(lb, rd, *ro, *rp, *rz, (zspan->rectmask)? *rm: 0, mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sdata->rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_tile(pa, sdata->edgerect, zspan->rectz);
|
|
}
|
|
|
|
/* main call for shading Delta Accum, for OSA */
|
|
/* supposed to be fully threadable! */
|
|
void zbufshadeDA_tile(RenderPart *pa)
|
|
{
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
ListBase psmlist= {NULL, NULL};
|
|
float *edgerect= NULL;
|
|
|
|
/* allocate the necessary buffers */
|
|
/* zbuffer inits these rects */
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
for (rl= rr->layers.first; rl; rl= rl->next) {
|
|
float *rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
|
|
|
|
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
|
|
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
|
|
|
|
/* initialize pixelstructs and edge buffer */
|
|
addpsmain(&psmlist);
|
|
pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd");
|
|
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
|
|
|
|
/* always fill visibility */
|
|
for (pa->sample=0; pa->sample<R.osa; pa->sample+=4) {
|
|
ZbufSolidData sdata;
|
|
|
|
sdata.rl= rl;
|
|
sdata.psmlist= &psmlist;
|
|
sdata.edgerect= edgerect;
|
|
zbuffer_solid(pa, rl, make_pixelstructs, &sdata);
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
/* shades solid */
|
|
if (rl->layflag & SCE_LAY_SOLID)
|
|
shadeDA_tile(pa, rl);
|
|
|
|
/* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
|
|
if (R.flag & R_LAMPHALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
lamphalo_tile(pa, rl);
|
|
|
|
/* halo before ztra, because ztra fills in zbuffer now */
|
|
if (R.flag & R_HALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
halo_tile(pa, rl);
|
|
|
|
/* transp layer */
|
|
if (R.flag & R_ZTRA || R.totstrand) {
|
|
if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
|
|
if (pa->fullresult.first) {
|
|
zbuffer_transp_shade(pa, rl, rect, &psmlist);
|
|
}
|
|
else {
|
|
unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */
|
|
|
|
/* allocate, but not free here, for asynchronous display of this rect in main thread */
|
|
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
|
|
|
|
/* swap for live updates, and it is used in zbuf.c!!! */
|
|
SWAP(float *, rl->acolrect, rect);
|
|
ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist);
|
|
SWAP(float *, rl->acolrect, rect);
|
|
|
|
/* zbuffer transp only returns ztramask if there's solid rendered */
|
|
if (ztramask)
|
|
solidmask= make_solid_mask(pa);
|
|
|
|
if (ztramask && solidmask) {
|
|
unsigned short *sps= solidmask, *spz= ztramask;
|
|
unsigned short fullmask= (1<<R.osa)-1;
|
|
float *fcol= rect;
|
|
float *acol= rl->acolrect;
|
|
int x;
|
|
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) {
|
|
if (*sps == fullmask)
|
|
addAlphaOverFloat(fcol, acol);
|
|
else
|
|
addAlphaOverFloatMask(fcol, acol, *sps, *spz);
|
|
}
|
|
}
|
|
else {
|
|
float *fcol= rect;
|
|
float *acol= rl->acolrect;
|
|
int x;
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
|
|
addAlphaOverFloat(fcol, acol);
|
|
}
|
|
}
|
|
if (solidmask) MEM_freeN(solidmask);
|
|
if (ztramask) MEM_freeN(ztramask);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* sun/sky */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
atm_tile(pa, rl);
|
|
|
|
/* sky before edge */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
sky_tile(pa, rl);
|
|
|
|
/* extra layers */
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_add(pa, rect, edgerect);
|
|
|
|
if (rl->passflag & SCE_PASS_VECTOR)
|
|
reset_sky_speed(pa, rl);
|
|
|
|
/* clamp alpha to 0..1 range, can go outside due to filter */
|
|
clamp_alpha_rgb_range(pa, rl);
|
|
|
|
/* free stuff within loop! */
|
|
MEM_freeN(pa->rectdaps); pa->rectdaps= NULL;
|
|
freeps(&psmlist);
|
|
|
|
if (edgerect) MEM_freeN(edgerect);
|
|
edgerect= NULL;
|
|
|
|
if (pa->rectmask) {
|
|
MEM_freeN(pa->rectmask);
|
|
pa->rectmask= NULL;
|
|
}
|
|
}
|
|
|
|
/* free all */
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* non OSA case, full tile render */
|
|
/* supposed to be fully threadable! */
|
|
void zbufshade_tile(RenderPart *pa)
|
|
{
|
|
ShadeSample ssamp;
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
PixStr ps;
|
|
float *edgerect= NULL;
|
|
|
|
/* fake pixel struct, to comply to osa render */
|
|
ps.next= NULL;
|
|
ps.mask= 0xFFFF;
|
|
|
|
/* zbuffer code clears/inits rects */
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
|
|
for (rl= rr->layers.first; rl; rl= rl->next) {
|
|
float *rect= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
|
|
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
|
|
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
|
|
|
|
/* general shader info, passes */
|
|
shade_sample_initialize(&ssamp, pa, rl);
|
|
|
|
zbuffer_solid(pa, rl, NULL, NULL);
|
|
|
|
if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */
|
|
|
|
/* edges only for solid part, ztransp doesn't support it yet anti-aliased */
|
|
if (rl->layflag & SCE_LAY_EDGE) {
|
|
if (R.r.mode & R_EDGE) {
|
|
edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge");
|
|
edge_enhance_tile(pa, edgerect, pa->rectz);
|
|
}
|
|
}
|
|
|
|
/* initialize scanline updates for main thread */
|
|
rr->renrect.ymin = 0;
|
|
rr->renlay= rl;
|
|
|
|
if (rl->layflag & SCE_LAY_SOLID) {
|
|
const float *fcol = rect;
|
|
const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz;
|
|
int x, y, offs=0, seed;
|
|
|
|
/* we set per pixel a fixed seed, for random AO and shadow samples */
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
|
|
/* irregular shadowb buffer creation */
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
|
|
if (R.occlusiontree)
|
|
cache_occ_samples(&R, pa, &ssamp);
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, ro++, rz++, rp++, fcol+=4, offs++) {
|
|
/* per pixel fixed seed */
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
if (*rp) {
|
|
ps.obi= *ro;
|
|
ps.facenr= *rp;
|
|
ps.z= *rz;
|
|
if (shade_samples(&ssamp, &ps, x, y)) {
|
|
/* combined and passes */
|
|
add_passes(rl, offs, ssamp.shi, ssamp.shr);
|
|
}
|
|
}
|
|
}
|
|
if (y&1)
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
|
|
if (R.occlusiontree)
|
|
free_occ_samples(&R, pa);
|
|
|
|
if (R.r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
}
|
|
|
|
/* disable scanline updating */
|
|
rr->renlay= NULL;
|
|
}
|
|
|
|
/* lamphalo after solid, before ztra, looks nicest because ztra does own halo */
|
|
if (R.flag & R_LAMPHALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
lamphalo_tile(pa, rl);
|
|
|
|
/* halo before ztra, because ztra fills in zbuffer now */
|
|
if (R.flag & R_HALO)
|
|
if (rl->layflag & SCE_LAY_HALO)
|
|
halo_tile(pa, rl);
|
|
|
|
if (R.flag & R_ZTRA || R.totstrand) {
|
|
if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
|
|
float *fcol, *acol;
|
|
int x;
|
|
|
|
/* allocate, but not free here, for asynchronous display of this rect in main thread */
|
|
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
|
|
|
|
/* swap for live updates */
|
|
SWAP(float *, rl->acolrect, rect);
|
|
zbuffer_transp_shade(pa, rl, rect, NULL);
|
|
SWAP(float *, rl->acolrect, rect);
|
|
|
|
fcol= rect; acol= rl->acolrect;
|
|
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
|
|
addAlphaOverFloat(fcol, acol);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* sun/sky */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
atm_tile(pa, rl);
|
|
|
|
/* sky before edge */
|
|
if (rl->layflag & SCE_LAY_SKY)
|
|
sky_tile(pa, rl);
|
|
|
|
if (!R.test_break(R.tbh)) {
|
|
if (rl->layflag & SCE_LAY_EDGE)
|
|
if (R.r.mode & R_EDGE)
|
|
edge_enhance_add(pa, rect, edgerect);
|
|
}
|
|
|
|
if (rl->passflag & SCE_PASS_VECTOR)
|
|
reset_sky_speed(pa, rl);
|
|
|
|
if (edgerect) MEM_freeN(edgerect);
|
|
edgerect= NULL;
|
|
|
|
if (pa->rectmask) {
|
|
MEM_freeN(pa->rectmask);
|
|
pa->rectmask= NULL;
|
|
}
|
|
}
|
|
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
}
|
|
|
|
/* SSS preprocess tile render, fully threadable */
|
|
typedef struct ZBufSSSHandle {
|
|
RenderPart *pa;
|
|
ListBase psmlist;
|
|
int totps;
|
|
} ZBufSSSHandle;
|
|
|
|
static void addps_sss(void *cb_handle, int obi, int facenr, int x, int y, int z)
|
|
{
|
|
ZBufSSSHandle *handle = cb_handle;
|
|
RenderPart *pa= handle->pa;
|
|
|
|
/* extra border for filter gives double samples on part edges,
|
|
* don't use those */
|
|
if (x<pa->crop || x>=pa->rectx-pa->crop)
|
|
return;
|
|
if (y<pa->crop || y>=pa->recty-pa->crop)
|
|
return;
|
|
|
|
if (pa->rectall) {
|
|
intptr_t *rs= pa->rectall + pa->rectx*y + x;
|
|
|
|
addps(&handle->psmlist, rs, obi, facenr, z, 0, 0);
|
|
handle->totps++;
|
|
}
|
|
if (pa->rectz) {
|
|
int *rz= pa->rectz + pa->rectx*y + x;
|
|
int *rp= pa->rectp + pa->rectx*y + x;
|
|
int *ro= pa->recto + pa->rectx*y + x;
|
|
|
|
if (z < *rz) {
|
|
if (*rp == 0)
|
|
handle->totps++;
|
|
*rz= z;
|
|
*rp= facenr;
|
|
*ro= obi;
|
|
}
|
|
}
|
|
if (pa->rectbackz) {
|
|
int *rz= pa->rectbackz + pa->rectx*y + x;
|
|
int *rp= pa->rectbackp + pa->rectx*y + x;
|
|
int *ro= pa->rectbacko + pa->rectx*y + x;
|
|
|
|
if (z >= *rz) {
|
|
if (*rp == 0)
|
|
handle->totps++;
|
|
*rz= z;
|
|
*rp= facenr;
|
|
*ro= obi;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void shade_sample_sss(ShadeSample *ssamp, Material *mat, ObjectInstanceRen *obi, VlakRen *vlr, int quad, float x, float y, float z, float *co, float color[3], float *area)
|
|
{
|
|
ShadeInput *shi= ssamp->shi;
|
|
ShadeResult shr;
|
|
float /* texfac,*/ /* UNUSED */ orthoarea, nor[3], alpha, sx, sy;
|
|
|
|
/* cache for shadow */
|
|
shi->samplenr= R.shadowsamplenr[shi->thread]++;
|
|
|
|
if (quad)
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 2, 3);
|
|
else
|
|
shade_input_set_triangle_i(shi, obi, vlr, 0, 1, 2);
|
|
|
|
/* center pixel */
|
|
sx = x + 0.5f;
|
|
sy = y + 0.5f;
|
|
|
|
/* we estimate the area here using shi->dxco and shi->dyco. we need to
|
|
* enabled shi->osatex these are filled. we compute two areas, one with
|
|
* the normal pointed at the camera and one with the original normal, and
|
|
* then clamp to avoid a too large contribution from a single pixel */
|
|
shi->osatex= 1;
|
|
|
|
copy_v3_v3(nor, shi->facenor);
|
|
calc_view_vector(shi->facenor, sx, sy);
|
|
normalize_v3(shi->facenor);
|
|
shade_input_set_viewco(shi, x, y, sx, sy, z);
|
|
orthoarea= len_v3(shi->dxco)*len_v3(shi->dyco);
|
|
|
|
copy_v3_v3(shi->facenor, nor);
|
|
shade_input_set_viewco(shi, x, y, sx, sy, z);
|
|
*area = min_ff(len_v3(shi->dxco) * len_v3(shi->dyco), 2.0f * orthoarea);
|
|
|
|
shade_input_set_uv(shi);
|
|
shade_input_set_normals(shi);
|
|
|
|
/* we don't want flipped normals, they screw up back scattering */
|
|
if (shi->flippednor)
|
|
shade_input_flip_normals(shi);
|
|
|
|
/* not a pretty solution, but fixes common cases */
|
|
if (shi->obr->ob && shi->obr->ob->transflag & OB_NEG_SCALE) {
|
|
negate_v3(shi->vn);
|
|
negate_v3(shi->vno);
|
|
negate_v3(shi->nmapnorm);
|
|
}
|
|
|
|
/* if nodetree, use the material that we are currently preprocessing
|
|
* instead of the node material */
|
|
if (shi->mat->nodetree && shi->mat->use_nodes)
|
|
shi->mat= mat;
|
|
|
|
/* init material vars */
|
|
shade_input_init_material(shi);
|
|
|
|
/* render */
|
|
shade_input_set_shade_texco(shi);
|
|
|
|
shade_samples_do_AO(ssamp);
|
|
shade_material_loop(shi, &shr);
|
|
|
|
copy_v3_v3(co, shi->co);
|
|
copy_v3_v3(color, shr.combined);
|
|
|
|
/* texture blending */
|
|
/* texfac= shi->mat->sss_texfac; */ /* UNUSED */
|
|
|
|
alpha= shr.combined[3];
|
|
*area *= alpha;
|
|
}
|
|
|
|
static void zbufshade_sss_free(RenderPart *pa)
|
|
{
|
|
#if 0
|
|
MEM_freeN(pa->rectall); pa->rectall= NULL;
|
|
freeps(&handle.psmlist);
|
|
#else
|
|
MEM_freeN(pa->rectz); pa->rectz= NULL;
|
|
MEM_freeN(pa->rectp); pa->rectp= NULL;
|
|
MEM_freeN(pa->recto); pa->recto= NULL;
|
|
MEM_freeN(pa->rectbackz); pa->rectbackz= NULL;
|
|
MEM_freeN(pa->rectbackp); pa->rectbackp= NULL;
|
|
MEM_freeN(pa->rectbacko); pa->rectbacko= NULL;
|
|
#endif
|
|
}
|
|
|
|
void zbufshade_sss_tile(RenderPart *pa)
|
|
{
|
|
Render *re= &R;
|
|
ShadeSample ssamp;
|
|
ZBufSSSHandle handle;
|
|
RenderResult *rr= pa->result;
|
|
RenderLayer *rl;
|
|
VlakRen *vlr;
|
|
Material *mat= re->sss_mat;
|
|
float (*co)[3], (*color)[3], *area, *fcol;
|
|
int x, y, seed, quad, totpoint;
|
|
const bool display = (re->r.scemode & (R_BUTS_PREVIEW | R_VIEWPORT_PREVIEW)) == 0;
|
|
int *ro, *rz, *rp, *rbo, *rbz, *rbp, lay;
|
|
#if 0
|
|
PixStr *ps;
|
|
intptr_t *rs;
|
|
int z;
|
|
#endif
|
|
|
|
/* setup pixelstr list and buffer for zbuffering */
|
|
handle.pa= pa;
|
|
handle.totps= 0;
|
|
|
|
#if 0
|
|
handle.psmlist.first= handle.psmlist.last= NULL;
|
|
addpsmain(&handle.psmlist);
|
|
|
|
pa->rectall= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "rectall");
|
|
#else
|
|
pa->recto= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "recto");
|
|
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
|
|
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
|
|
pa->rectbacko= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbacko");
|
|
pa->rectbackp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackp");
|
|
pa->rectbackz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectbackz");
|
|
#endif
|
|
|
|
/* setup shade sample with correct passes */
|
|
memset(&ssamp, 0, sizeof(ssamp));
|
|
shade_sample_initialize(&ssamp, pa, rr->layers.first);
|
|
ssamp.tot= 1;
|
|
|
|
for (rl=rr->layers.first; rl; rl=rl->next) {
|
|
ssamp.shi[0].lay |= rl->lay;
|
|
ssamp.shi[0].layflag |= rl->layflag;
|
|
ssamp.shi[0].passflag |= rl->passflag;
|
|
ssamp.shi[0].combinedflag |= ~rl->pass_xor;
|
|
}
|
|
|
|
rl= rr->layers.first;
|
|
ssamp.shi[0].passflag |= SCE_PASS_RGBA|SCE_PASS_COMBINED;
|
|
ssamp.shi[0].combinedflag &= ~(SCE_PASS_SPEC);
|
|
ssamp.shi[0].mat_override= NULL;
|
|
ssamp.shi[0].light_override= NULL;
|
|
lay= ssamp.shi[0].lay;
|
|
|
|
/* create the pixelstrs to be used later */
|
|
zbuffer_sss(pa, lay, &handle, addps_sss);
|
|
|
|
if (handle.totps==0) {
|
|
zbufshade_sss_free(pa);
|
|
return;
|
|
}
|
|
|
|
fcol= RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, R.viewname);
|
|
|
|
co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo");
|
|
color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor");
|
|
area= MEM_mallocN(sizeof(float)*handle.totps, "SSSArea");
|
|
|
|
#if 0
|
|
/* create ISB (does not work currently!) */
|
|
if (re->r.mode & R_SHADOW)
|
|
ISB_create(pa, NULL);
|
|
#endif
|
|
|
|
if (display) {
|
|
/* initialize scanline updates for main thread */
|
|
rr->renrect.ymin = 0;
|
|
rr->renlay= rl;
|
|
}
|
|
|
|
seed= pa->rectx*pa->disprect.ymin;
|
|
#if 0
|
|
rs= pa->rectall;
|
|
#else
|
|
rz= pa->rectz;
|
|
rp= pa->rectp;
|
|
ro= pa->recto;
|
|
rbz= pa->rectbackz;
|
|
rbp= pa->rectbackp;
|
|
rbo= pa->rectbacko;
|
|
#endif
|
|
totpoint= 0;
|
|
|
|
for (y=pa->disprect.ymin; y<pa->disprect.ymax; y++, rr->renrect.ymax++) {
|
|
for (x=pa->disprect.xmin; x<pa->disprect.xmax; x++, fcol+=4) {
|
|
/* per pixel fixed seed */
|
|
BLI_thread_srandom(pa->thread, seed++);
|
|
|
|
#if 0
|
|
if (rs) {
|
|
/* for each sample in this pixel, shade it */
|
|
for (ps = (PixStr *)(*rs); ps; ps=ps->next) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[ps->obi];
|
|
ObjectRen *obr= obi->obr;
|
|
vlr= RE_findOrAddVlak(obr, (ps->facenr-1) & RE_QUAD_MASK);
|
|
quad= (ps->facenr & RE_QUAD_OFFS);
|
|
z= ps->z;
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, z,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
totpoint++;
|
|
|
|
add_v3_v3(fcol, color);
|
|
fcol[3]= 1.0f;
|
|
}
|
|
|
|
rs++;
|
|
}
|
|
#else
|
|
if (rp) {
|
|
if (*rp != 0) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[*ro];
|
|
ObjectRen *obr= obi->obr;
|
|
|
|
/* shade front */
|
|
vlr= RE_findOrAddVlak(obr, (*rp-1) & RE_QUAD_MASK);
|
|
quad= ((*rp) & RE_QUAD_OFFS);
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rz,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
add_v3_v3(fcol, color[totpoint]);
|
|
fcol[3]= 1.0f;
|
|
totpoint++;
|
|
}
|
|
|
|
rp++; rz++; ro++;
|
|
}
|
|
|
|
if (rbp) {
|
|
if (*rbp != 0 && !(*rbp == *(rp-1) && *rbo == *(ro-1))) {
|
|
ObjectInstanceRen *obi= &re->objectinstance[*rbo];
|
|
ObjectRen *obr= obi->obr;
|
|
|
|
/* shade back */
|
|
vlr= RE_findOrAddVlak(obr, (*rbp-1) & RE_QUAD_MASK);
|
|
quad= ((*rbp) & RE_QUAD_OFFS);
|
|
|
|
shade_sample_sss(&ssamp, mat, obi, vlr, quad, x, y, *rbz,
|
|
co[totpoint], color[totpoint], &area[totpoint]);
|
|
|
|
/* to indicate this is a back sample */
|
|
area[totpoint]= -area[totpoint];
|
|
|
|
add_v3_v3(fcol, color[totpoint]);
|
|
fcol[3]= 1.0f;
|
|
totpoint++;
|
|
}
|
|
|
|
rbz++; rbp++; rbo++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (y&1)
|
|
if (re->test_break(re->tbh)) break;
|
|
}
|
|
|
|
/* note: after adding we do not free these arrays, sss keeps them */
|
|
if (totpoint > 0) {
|
|
sss_add_points(re, co, color, area, totpoint);
|
|
}
|
|
else {
|
|
MEM_freeN(co);
|
|
MEM_freeN(color);
|
|
MEM_freeN(area);
|
|
}
|
|
|
|
#if 0
|
|
if (re->r.mode & R_SHADOW)
|
|
ISB_free(pa);
|
|
#endif
|
|
|
|
if (display) {
|
|
/* display active layer */
|
|
rr->renrect.ymin=rr->renrect.ymax = 0;
|
|
rr->renlay= render_get_active_layer(&R, rr);
|
|
}
|
|
|
|
zbufshade_sss_free(pa);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static void renderhalo_post(RenderResult *rr, float *rectf, HaloRen *har) /* postprocess version */
|
|
{
|
|
float dist, xsq, ysq, xn, yn, colf[4], *rectft, *rtf;
|
|
float haloxs, haloys;
|
|
int minx, maxx, miny, maxy, x, y;
|
|
|
|
/* calculate the disprect mapped coordinate for halo. note: rectx is disprect corrected */
|
|
haloxs= har->xs - R.disprect.xmin;
|
|
haloys= har->ys - R.disprect.ymin;
|
|
|
|
har->miny= miny= haloys - har->rad/R.ycor;
|
|
har->maxy= maxy= haloys + har->rad/R.ycor;
|
|
|
|
if (maxy < 0) {
|
|
/* pass */
|
|
}
|
|
else if (rr->recty < miny) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
minx = floor(haloxs - har->rad);
|
|
maxx = ceil(haloxs + har->rad);
|
|
|
|
if (maxx < 0) {
|
|
/* pass */
|
|
}
|
|
else if (rr->rectx < minx) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
if (minx<0) minx= 0;
|
|
if (maxx>=rr->rectx) maxx= rr->rectx-1;
|
|
if (miny<0) miny= 0;
|
|
if (maxy>rr->recty) maxy= rr->recty;
|
|
|
|
rectft= rectf+ 4*rr->rectx*miny;
|
|
|
|
for (y=miny; y<maxy; y++) {
|
|
|
|
rtf= rectft+4*minx;
|
|
|
|
yn= (y - haloys)*R.ycor;
|
|
ysq= yn*yn;
|
|
|
|
for (x=minx; x<=maxx; x++) {
|
|
xn= x - haloxs;
|
|
xsq= xn*xn;
|
|
dist= xsq+ysq;
|
|
if (dist<har->radsq) {
|
|
|
|
if (shadeHaloFloat(har, colf, 0x7FFFFF, dist, xn, yn, har->flarec))
|
|
addalphaAddfacFloat(rtf, colf, har->add);
|
|
}
|
|
rtf+=4;
|
|
}
|
|
|
|
rectft+= 4*rr->rectx;
|
|
|
|
if (R.test_break(R.tbh)) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static void renderflare(RenderResult *rr, float *rectf, HaloRen *har)
|
|
{
|
|
extern const float hashvectf[];
|
|
HaloRen fla;
|
|
Material *ma;
|
|
const float *rc;
|
|
float rad, alfa, visifac, vec[3];
|
|
int b, type;
|
|
|
|
fla= *har;
|
|
fla.linec= fla.ringc= fla.flarec= 0;
|
|
|
|
rad= har->rad;
|
|
alfa= har->alfa;
|
|
|
|
visifac= R.ycor*(har->pixels);
|
|
/* all radials added / r^3 == 1.0f! */
|
|
visifac /= (har->rad*har->rad*har->rad);
|
|
visifac*= visifac;
|
|
|
|
ma= har->mat;
|
|
|
|
/* first halo: just do */
|
|
|
|
har->rad= rad*ma->flaresize*visifac;
|
|
har->radsq= har->rad*har->rad;
|
|
har->zs= fla.zs= 0;
|
|
|
|
har->alfa= alfa*visifac;
|
|
|
|
renderhalo_post(rr, rectf, har);
|
|
|
|
/* next halo's: the flares */
|
|
rc= hashvectf + ma->seed2;
|
|
|
|
for (b=1; b<har->flarec; b++) {
|
|
|
|
fla.r = fabsf(rc[0]);
|
|
fla.g = fabsf(rc[1]);
|
|
fla.b = fabsf(rc[2]);
|
|
fla.alfa= ma->flareboost*fabsf(alfa*visifac*rc[3]);
|
|
fla.hard= 20.0f + fabsf(70.0f*rc[7]);
|
|
fla.tex= 0;
|
|
|
|
type= (int)(fabsf(3.9f*rc[6]));
|
|
|
|
fla.rad = ma->subsize * sqrtf(fabsf(2.0f * har->rad * rc[4]));
|
|
|
|
if (type==3) {
|
|
fla.rad*= 3.0f;
|
|
fla.rad+= R.rectx/10;
|
|
}
|
|
|
|
fla.radsq= fla.rad*fla.rad;
|
|
|
|
vec[0]= 1.4f*rc[5]*(har->xs-R.winx/2);
|
|
vec[1]= 1.4f*rc[5]*(har->ys-R.winy/2);
|
|
vec[2]= 32.0f*sqrtf(vec[0]*vec[0] + vec[1]*vec[1] + 1.0f);
|
|
|
|
fla.xs= R.winx/2 + vec[0] + (1.2f+rc[8])*R.rectx*vec[0]/vec[2];
|
|
fla.ys= R.winy/2 + vec[1] + (1.2f+rc[8])*R.rectx*vec[1]/vec[2];
|
|
|
|
if (R.flag & R_SEC_FIELD) {
|
|
if (R.r.mode & R_ODDFIELD) fla.ys += 0.5f;
|
|
else fla.ys -= 0.5f;
|
|
}
|
|
if (type & 1) fla.type= HA_FLARECIRC;
|
|
else fla.type= 0;
|
|
renderhalo_post(rr, rectf, &fla);
|
|
|
|
fla.alfa*= 0.5f;
|
|
if (type & 2) fla.type= HA_FLARECIRC;
|
|
else fla.type= 0;
|
|
renderhalo_post(rr, rectf, &fla);
|
|
|
|
rc+= 7;
|
|
}
|
|
}
|
|
|
|
/* needs recode... integrate this better! */
|
|
void add_halo_flare(Render *re)
|
|
{
|
|
RenderResult *rr= re->result;
|
|
RenderLayer *rl;
|
|
HaloRen *har;
|
|
int a, mode;
|
|
float *rect;
|
|
|
|
/* for now, we get the first renderlayer in list with halos set */
|
|
for (rl= rr->layers.first; rl; rl= rl->next) {
|
|
bool do_draw = false;
|
|
|
|
if ((rl->layflag & SCE_LAY_HALO) == 0)
|
|
continue;
|
|
|
|
rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, re->viewname);
|
|
|
|
if (rect==NULL)
|
|
continue;
|
|
|
|
mode= R.r.mode;
|
|
R.r.mode &= ~R_PANORAMA;
|
|
|
|
project_renderdata(&R, projectverto, 0, 0, 0);
|
|
|
|
for (a=0; a<R.tothalo; a++) {
|
|
har= R.sortedhalos[a];
|
|
|
|
if (har->flarec && (har->lay & rl->lay)) {
|
|
do_draw = true;
|
|
renderflare(rr, rect, har);
|
|
}
|
|
}
|
|
|
|
if (do_draw) {
|
|
/* weak... the display callback wants an active renderlayer pointer... */
|
|
rr->renlay= rl;
|
|
re->display_update(re->duh, rr, NULL);
|
|
}
|
|
|
|
R.r.mode= mode;
|
|
}
|
|
}
|
|
|
|
void render_internal_update_passes(RenderEngine *engine, Scene *scene, SceneRenderLayer *srl)
|
|
{
|
|
int type;
|
|
|
|
RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
|
|
|
|
#define CHECK_PASS(name, channels, chanid) \
|
|
if (srl->passflag & (SCE_PASS_ ## name)) { \
|
|
if (channels == 4) type = SOCK_RGBA; \
|
|
else if (channels == 3) type = SOCK_VECTOR; \
|
|
else type = SOCK_FLOAT; \
|
|
RE_engine_register_pass(engine, scene, srl, RE_PASSNAME_ ## name, channels, chanid, type); \
|
|
}
|
|
|
|
CHECK_PASS(Z, 1, "Z");
|
|
CHECK_PASS(VECTOR, 4, "XYZW");
|
|
CHECK_PASS(NORMAL, 3, "XYZ");
|
|
CHECK_PASS(UV, 3, "UVA");
|
|
CHECK_PASS(RGBA, 4, "RGBA");
|
|
CHECK_PASS(EMIT, 3, "RGB");
|
|
CHECK_PASS(DIFFUSE, 3, "RGB");
|
|
CHECK_PASS(SPEC, 3, "RGB");
|
|
CHECK_PASS(AO, 3, "RGB");
|
|
CHECK_PASS(ENVIRONMENT, 3, "RGB");
|
|
CHECK_PASS(INDIRECT, 3, "RGB");
|
|
CHECK_PASS(SHADOW, 3, "RGB");
|
|
CHECK_PASS(REFLECT, 3, "RGB");
|
|
CHECK_PASS(REFRACT, 3, "RGB");
|
|
CHECK_PASS(INDEXOB, 1, "X");
|
|
CHECK_PASS(INDEXMA, 1, "X");
|
|
CHECK_PASS(MIST, 1, "Z");
|
|
|
|
#undef CHECK_PASS
|
|
}
|