Recoded Panorama rendering.

The old implementation was added quite hackish (talking about 10 yr ago).
You also had to make a small image slice, which was extended Xparts in
size. That also required to adjust the camera angle. Very clumsy.

Now; when enabling the Panorama option, it will automatically apply the
panorama effect on the vertically aligned tiles. You can just enable or
disable the "Pano" button, to get a subtle lens effect like this:

(without pano)
http://www.blender.org/bf/rt.jpg
(with pano)
http://www.blender.org/bf/rt1.jpg

For Panorama render, the minimum slice size has been hardcoded to be 8
pixels. The XParts button goes up to 512 to allow that. In practice,
rendering 64 slices will already give very good images for a wide angle
lens of 90 degrees, the curvature of straight lines then is equal to
a circle of 256 points.
Rendering a full 360 degree panorama you do by creating an extreme wide
angle camera. The theory says camera-lens 5 should do 360 degrees, but
for some reason my tests reveil it's 5.1... there's a rounding error
somewhere, maybe related to the clipping plane start? Will look at that
later. :)

Also note that for each Xpart slice, the entire database needs to be
rotated around camera to correct for panorama, on huge scenes that might
give some overhead.

Threaded render goes fine for Panorama too, but it can only render the
vertically aligned parts in parallel. For the next panorama slice it has
to wait for all threads of the current slice to be ready.

On reading old files, I convert the settings to match as closely as
possible the new situation.
Since I cannot bump up the version #, the code detects for old panorama
by checking for the image size. If image width is smaller than height, it
assumes it's an old file (only if Panoroma option was set).
This commit is contained in:
Ton Roosendaal 2006-02-27 12:39:36 +00:00
parent 534ee9e190
commit f68b0ddb2a
13 changed files with 169 additions and 97 deletions

@ -5335,6 +5335,19 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
}
}
}
if(sce->r.mode & R_PANORAMA) {
/* all these checks to ensure saved files with cvs version keep working... */
if(sce->r.xsch < sce->r.ysch) {
Object *obc= newlibadr(fd, lib, sce->camera);
if(obc && obc->type==OB_CAMERA) {
Camera *cam= newlibadr(fd, lib, obc->data);
if(cam->lens>=10.0f) {
sce->r.xsch*= sce->r.xparts;
cam->lens*= (float)sce->r.ysch/(float)sce->r.xsch;
}
}
}
}
}
for(ntree= main->nodetree.first; ntree; ntree= ntree->id.next)

@ -408,7 +408,7 @@ typedef struct Scene {
#define R_RADIO 0x0100
#define R_BORDER 0x0200
#define R_PANORAMA 0x0400
#define R_MOVIECROP 0x0800
/* was moviecrop 0x0800 */
#define R_COSMO 0x1000
#define R_ODDFIELD 0x2000
#define R_MBLUR 0x4000

@ -1526,8 +1526,10 @@ PyObject *RenderData_SetRenderer( BPy_RenderData * self, PyObject * args )
//------------------------------------RenderData.EnableCropping() -------
PyObject *RenderData_EnableCropping( BPy_RenderData * self, PyObject * args )
{
return M_Render_BitToggleInt( args, R_MOVIECROP,
/* return M_Render_BitToggleInt( args, R_MOVIECROP,
&self->renderContext->mode );
*/
printf("cropping option is now default, obsolete\n");
}
//------------------------------------RenderData.SetImageType() ---------

@ -71,9 +71,9 @@ typedef struct RenderPart
rcti disprect; /* part coordinates within total picture */
int rectx, recty; /* the size */
volatile short crop, ready; /* crop is amount of pixels we crop, for filter */
volatile short sample, nr; /* sample can be used by zbuffers, nr is partnr */
volatile short thread; /* thread id */
short crop, ready; /* crop is amount of pixels we crop, for filter */
short sample, nr; /* sample can be used by zbuffers, nr is partnr */
short thread; /* thread id */
} RenderPart;
@ -108,10 +108,16 @@ struct Render
/* final picture width and height (within disprect) */
int rectx, recty;
/* real maximum amount of xparts/yparts after correction for minimum */
int xparts, yparts;
/* real maximum size of parts after correction for minimum
partx*xparts can be larger than rectx, in that case last part is smaller */
int partx, party;
/* correction values for pixels or view */
float ycor, viewfac;
float bluroffsx, bluroffsy;
float panosi, panoco;
float panophi, panosi, panoco, panodxp, panodxv;
/* Matrices */
float grvec[3]; /* for world */

@ -56,7 +56,7 @@ void free_renderdata_tables(struct Render *re);
void free_renderdata_vertnodes(struct VertTableNode *vertnodes);
void set_normalflags(Render *re);
void project_renderdata(struct Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int part);
void project_renderdata(struct Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs);
/* functions are not exported... so wrong names */

@ -35,6 +35,7 @@ struct RenderResult;
struct RenderLayer;
struct RenderLayer *render_get_active_layer(struct Render *re, struct RenderResult *rr);
float panorama_pixel_rot(struct Render *re);
#define PASS_VECTOR_MAX 10000.0f

@ -579,7 +579,7 @@ void freeparts(Render *re)
void initparts(Render *re)
{
int nr, xd, yd, xpart, ypart, xparts, yparts;
int nr, xd, yd, partx, party, xparts, yparts;
int xminb, xmaxb, yminb, ymaxb;
freeparts(re);
@ -599,14 +599,33 @@ void initparts(Render *re)
yparts= re->r.yparts;
/* mininum part size */
if(re->rectx/xparts < 64)
xparts= 1 + re->rectx/64;
if(re->r.mode & R_PANORAMA) {
if(re->rectx/xparts < 8)
xparts= 1 + re->rectx/8;
}
else
if(re->rectx/xparts < 64)
xparts= 1 + re->rectx/64;
if(re->recty/yparts < 64)
yparts= 1 + re->recty/64;
/* part size */
xpart= re->rectx/xparts;
ypart= re->recty/yparts;
partx= re->rectx/xparts;
party= re->recty/yparts;
/* if remainder pixel, add one, then parts are more equal in size for large panoramas */
if(re->rectx % partx)
partx++;
re->xparts= xparts;
re->yparts= yparts;
re->partx= partx;
re->party= party;
/* calculate rotation factor of 1 pixel */
if(re->r.mode & R_PANORAMA)
re->panophi= panorama_pixel_rot(re);
for(nr=0; nr<xparts*yparts; nr++) {
rcti disprect;
@ -615,19 +634,19 @@ void initparts(Render *re)
xd= (nr % xparts);
yd= (nr-xd)/xparts;
disprect.xmin= xminb+ xd*xpart;
disprect.ymin= yminb+ yd*ypart;
disprect.xmin= xminb+ xd*partx;
disprect.ymin= yminb+ yd*party;
/* ensure we cover the entire picture, so last parts go to end */
if(xd<xparts-1) {
disprect.xmax= disprect.xmin + xpart;
disprect.xmax= disprect.xmin + partx;
if(disprect.xmax > xmaxb)
disprect.xmax = xmaxb;
}
else disprect.xmax= xmaxb;
if(yd<yparts-1) {
disprect.ymax= disprect.ymin + ypart;
disprect.ymax= disprect.ymin + party;
if(disprect.ymax > ymaxb)
disprect.ymax = ymaxb;
}

@ -101,7 +101,7 @@
static struct ListBase RenderList= {NULL, NULL};
/* hardcopy of current render, used while rendering for speed */
volatile Render R;
Render R;
//static SDL_mutex *exrtile_lock= NULL;
@ -274,7 +274,7 @@ float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype)
/* will read info from Render *re to define layers */
/* called in threads */
/* re->winx,winy is coordinate space of entire image, partrct the part within */
static RenderResult *new_render_result(volatile Render *re, rcti *partrct, int crop)
static RenderResult *new_render_result(Render *re, rcti *partrct, int crop)
{
RenderResult *rr;
RenderLayer *rl;
@ -431,7 +431,7 @@ static void merge_render_result(RenderResult *rr, RenderResult *rrpart)
}
static void save_render_result_tile(volatile Render *re, RenderPart *pa)
static void save_render_result_tile(Render *re, RenderPart *pa)
{
RenderResult *rrpart= pa->result;
RenderLayer *rlp;
@ -881,7 +881,69 @@ static void render_tile_processor(Render *re, int firsttile)
freeparts(re);
}
static RenderPart *find_next_part(Render *re)
/* calculus for how much 1 pixel rendered should rotate the 3d geometry */
/* is not that simple, needs to be corrected for errors of larger viewplane sizes */
/* called in initrender.c, initparts() */
float panorama_pixel_rot(Render *re)
{
float psize, phi, xfac;
/* size of 1 pixel mapped to viewplane coords */
psize= (re->viewplane.xmax-re->viewplane.xmin)/(float)re->winx;
/* angle of a pixel */
phi= atan(psize/re->clipsta);
/* correction factor for viewplane shifting, first calculate how much the viewplane angle is */
xfac= ((re->viewplane.xmax-re->viewplane.xmin))/(float)re->xparts;
xfac= atan(0.5f*xfac/re->clipsta);
/* and how much the same viewplane angle is wrapped */
psize= 0.5f*phi*((float)re->partx);
/* the ratio applied to final per-pixel angle */
phi*= xfac/psize;
return phi;
}
/* call when all parts stopped rendering, to find the next Y slice */
/* if slice found, it rotates the dbase */
static RenderPart *find_next_pano_slice(Render *re, int *minx, rctf *viewplane)
{
RenderPart *pa, *best= NULL;
*minx= re->winx;
/* most left part of the non-rendering parts */
for(pa= re->parts.first; pa; pa= pa->next) {
if(pa->ready==0 && pa->nr==0) {
if(pa->disprect.xmin < *minx) {
best= pa;
*minx= pa->disprect.xmin;
}
}
}
if(best) {
float phi= panorama_pixel_rot(re);
R.panodxp= (re->winx - (best->disprect.xmin + best->disprect.xmax) )/2;
R.panodxv= ((viewplane->xmax-viewplane->xmin)*R.panodxp)/(float)R.winx;
/* shift viewplane */
R.viewplane.xmin = viewplane->xmin + R.panodxv;
R.viewplane.xmax = viewplane->xmax + R.panodxv;
RE_SetWindow(re, &R.viewplane, R.clipsta, R.clipend);
Mat4CpyMat4(R.winmat, re->winmat);
/* rotate database according to part coordinates */
project_renderdata(re, projectverto, 1, -R.panodxp*phi);
R.panosi= sin(R.panodxp*phi);
R.panoco= cos(R.panodxp*phi);
}
return best;
}
static RenderPart *find_next_part(Render *re, int minx)
{
RenderPart *pa, *best= NULL;
int centx=re->winx/2, centy=re->winy/2, tot=1;
@ -906,8 +968,16 @@ static RenderPart *find_next_part(Render *re)
disty= centy - (pa->disprect.ymin+pa->disprect.ymax)/2;
distx= (int)sqrt(distx*distx + disty*disty);
if(distx<mindist) {
best= pa;
mindist= distx;
if(re->r.mode & R_PANORAMA) {
if(pa->disprect.xmin==minx) {
best= pa;
mindist= distx;
}
}
else {
best= pa;
mindist= distx;
}
}
}
}
@ -929,7 +999,8 @@ static void threaded_tile_processor(Render *re)
ListBase threads;
RenderPart *pa, *nextpa;
RenderResult *rr= re->result;
int maxthreads, rendering=1, counter= 1, drawtimer=0, hasdrawn;
rctf viewplane= re->viewplane;
int maxthreads, rendering=1, counter= 1, drawtimer=0, hasdrawn, minx;
if(rr==NULL)
return;
@ -937,7 +1008,7 @@ static void threaded_tile_processor(Render *re)
return;
if(rr->exrhandle) {
IMB_exrtile_begin_write(rr->exrhandle, "/tmp/render.exr", rr->rectx, rr->recty, rr->rectx/re->r.xparts, rr->recty/re->r.yparts);
IMB_exrtile_begin_write(rr->exrhandle, "/tmp/render.exr", rr->rectx, rr->recty, rr->rectx/re->xparts, rr->recty/re->yparts);
// exrtile_lock = SDL_CreateMutex();
}
@ -953,18 +1024,27 @@ static void threaded_tile_processor(Render *re)
/* set threadsafe break */
R.test_break= thread_break;
/* timer loop demands to sleep when no parts are left */
nextpa= find_next_part(re);
/* timer loop demands to sleep when no parts are left, so we enter loop with a part */
if(re->r.mode & R_PANORAMA)
nextpa= find_next_pano_slice(re, &minx, &viewplane);
else
nextpa= find_next_part(re, 0);
while(rendering) {
if(nextpa && BLI_available_threads(&threads) && !re->test_break()) {
if(re->test_break())
PIL_sleep_ms(50);
else if(nextpa && BLI_available_threads(&threads)) {
drawtimer= 0;
nextpa->nr= counter++; /* for nicest part, and for stats */
nextpa->thread= BLI_available_thread_index(&threads); /* sample index */
BLI_insert_thread(&threads, nextpa);
nextpa= find_next_part(re);
nextpa= find_next_part(re, minx);
}
else if(re->r.mode & R_PANORAMA) {
if(nextpa==NULL && BLI_available_threads(&threads)==maxthreads)
nextpa= find_next_pano_slice(re, &minx, &viewplane);
}
else {
PIL_sleep_ms(50);
@ -1042,7 +1122,6 @@ void render_one_frame(Render *re)
RE_Database_FromScene(re, re->scene, 1);
threaded_tile_processor(re);
//render_tile_processor(re, 0);
/* free all render verts etc */
RE_Database_Free(re);
@ -1226,10 +1305,6 @@ static int is_rendering_allowed(Render *re)
re->error("No border supported for Panorama");
return 0;
}
if(re->r.yparts>1) {
re->error("No Y-Parts supported for Panorama");
return 0;
}
if(re->r.mode & R_ORTHO) {
re->error("No Ortho render possible for Panorama");
return 0;
@ -1244,17 +1319,7 @@ static int is_rendering_allowed(Render *re)
}
}
if(re->r.xparts*re->r.yparts>=2 && (re->r.mode & R_MOVIECROP) && (re->r.mode & R_BORDER)) {
re->error("Combination of border, crop and parts not allowed");
return 0;
}
if(re->r.yparts>1 && (re->r.mode & R_PANORAMA)) {
re->error("No Y-Parts supported for Panorama");
return 0;
}
/* check valid camera */
/* check valid camera */
if(re->scene->camera==NULL)
re->scene->camera= scene_find_camera(re->scene);
if(re->scene->camera==NULL) {
@ -1278,8 +1343,6 @@ static int render_initialize_from_scene(Render *re, Scene *scene)
/* calculate actual render result and display size */
winx= (scene->r.size*scene->r.xsch)/100;
winy= (scene->r.size*scene->r.ysch)/100;
// if(scene->r.mode & R_PANORAMA)
// winx*= scene->r.xparts;
/* only in movie case we render smaller part */
if(scene->r.mode & R_BORDER) {

@ -525,13 +525,6 @@ void shadeSkyPixel(float *collector, float fx, float fy, float *rco)
}
}
if(R.r.mode & R_PANORAMA) {
float u= view[0]; float v= view[2];
view[0]= R.panoco*u + R.panosi*v;
view[2]= -R.panosi*u + R.panoco*v;
}
/* get sky colour in the collector */
shadeSkyPixelFloat(collector, rco, view, dxyview);
collector[3] = 1.0f;

@ -84,10 +84,16 @@ extern struct Render R;
void calc_view_vector(float *view, float x, float y)
{
view[2]= -R.clipsta;
if(R.r.mode & R_ORTHO) {
view[0]= view[1]= 0.0;
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*(R.viewplane.xmax - R.viewplane.xmin);
@ -100,16 +106,14 @@ void calc_view_vector(float *view, float x, float y)
// else view[1]= (y+R.ystart+1.0)*R.ycor;
// }
// else view[1]= (y+R.ystart+R.bluroffsy+0.5)*R.ycor;
}
view[2]= -R.clipsta;
if(R.r.mode & R_PANORAMA) {
float u= view[0]; float v= view[2];
view[0]= R.panoco*u + R.panosi*v;
view[2]= -R.panosi*u + R.panoco*v;
}
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;
}
}
}
#if 0

@ -482,20 +482,6 @@ HaloRen *RE_inithalo(Render *re, Material *ma, float *vec, float *vec1,
/* -------------------------- operations on entire database ----------------------- */
static float get_pano_rot(Render *re, int part)
{
static float alpha= 1.0;
/* part==0 init all */
if(part==0) {
alpha= ((float)re->r.xsch)/re->viewfac;
alpha= 2.0*atan(alpha/2.0);
}
/* rotate it all around the y-as with phi degrees */
return 0.5*(re->r.xparts-1)*alpha + part*alpha;
}
/* ugly function for halos in panorama */
static int panotestclip(Render *re, int do_pano, float *v)
{
@ -513,7 +499,7 @@ static int panotestclip(Render *re, int do_pano, float *v)
if( v[1]>abs4) c+=4;
else if( v[1]< -abs4) c+=8;
abs4*= re->r.xparts;
abs4*= re->xparts;
if( v[0]>abs4) c+=2;
else if( v[0]< -abs4) c+=1;
@ -530,7 +516,7 @@ static int panotestclip(Render *re, int do_pano, float *v)
- shadow buffering (shadbuf.c)
*/
void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, int part)
void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4], float *), int do_pano, float xoffs)
{
VlakRen *vlr = NULL;
VertRen *ver = NULL;
@ -539,7 +525,7 @@ void project_renderdata(Render *re, void (*projectfunc)(float *, float mat[][4],
int a;
if(do_pano) {
float panophi= get_pano_rot(re, part);
float panophi= xoffs;
re->panosi= sin(panophi);
re->panoco= cos(panophi);

@ -790,7 +790,7 @@ void do_render_panels(unsigned short event)
allqueue(REDRAWBUTSSCENE, 0);
break;
case B_PR_PANO:
G.scene->r.xsch= 36;
G.scene->r.xsch= 576;
G.scene->r.ysch= 176;
G.scene->r.xasp= 115;
G.scene->r.yasp= 100;
@ -1290,7 +1290,7 @@ static void render_panel_render(void)
uiBlockEndAlign(block);
uiBlockBeginAlign(block);
uiDefButS(block, NUM,B_DIFF,"Xparts:", 369,46,95,29,&G.scene->r.xparts,2.0, 64.0, 0, 0, "Sets the number of horizontal parts to render image in (For panorama sets number of camera slices)");
uiDefButS(block, NUM,B_DIFF,"Xparts:", 369,46,95,29,&G.scene->r.xparts,2.0, 512.0, 0, 0, "Sets the number of horizontal parts to render image in (For panorama sets number of camera slices)");
uiDefButS(block, NUM,B_DIFF,"Yparts:", 465,46,95,29,&G.scene->r.yparts,2.0, 64.0, 0, 0, "Sets the number of vertical parts to render image in");
uiBlockEndAlign(block);

@ -305,24 +305,9 @@ void start_movie(void)
numfields = 2;
}
}
if(R.r.mode & R_MOVIECROP) {
if (ntsc) {
if (R.rectx > 640) mv_outx = 720;
else mv_outx = 640;
mv_outy = 480;
numfields = 2;
} else {
if (R.rectx > 720) mv_outx = 768;
else mv_outx = 720;
mv_outy = 576;
numfields = 2;
}
}
qualnow = R.r.quality;
fd = open(name, O_BINARY|O_RDWR);
if (fd != -1) {
if (flock(fd, LOCK_EX) == -1) report_flock();