Cycles microdisplacement: perform subdivision dicing in raster space

NOTE: this is only the first of many patches towards completing the subdivison
and displacement system in Cycles. These patches will be reviewed and committed
one by one over the coming weeks.

Reviewed By: brecht, sergey

Differential Revision: https://developer.blender.org/D1909
This commit is contained in:
Mai Lavelle 2016-04-11 22:49:09 +02:00 committed by Brecht Van Lommel
parent 7d033717ad
commit ebfdd7da83
9 changed files with 117 additions and 13 deletions

@ -938,8 +938,8 @@ class CyclesMeshSettings(bpy.types.PropertyGroup):
) )
cls.dicing_rate = FloatProperty( cls.dicing_rate = FloatProperty(
name="Dicing Rate", name="Dicing Rate",
description="", description="Size of a micropolygon in pixels",
min=0.001, max=1000.0, min=0.1, max=1000.0,
default=1.0, default=1.0,
) )

@ -360,6 +360,12 @@ static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int
blender_camera_viewplane(bcam, width, height, blender_camera_viewplane(bcam, width, height,
&cam->viewplane, &aspectratio, &sensor_size); &cam->viewplane, &aspectratio, &sensor_size);
cam->width = bcam->full_width;
cam->height = bcam->full_height;
cam->full_width = width;
cam->full_height = height;
/* panorama sensor */ /* panorama sensor */
if(bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) { if(bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) {
float fit_xratio = (float)bcam->full_width*bcam->pixelaspect.x; float fit_xratio = (float)bcam->full_width*bcam->pixelaspect.x;

@ -18,6 +18,7 @@
#include "mesh.h" #include "mesh.h"
#include "object.h" #include "object.h"
#include "scene.h" #include "scene.h"
#include "camera.h"
#include "blender_sync.h" #include "blender_sync.h"
#include "blender_session.h" #include "blender_session.h"
@ -655,6 +656,7 @@ static void create_mesh(Scene *scene,
static void create_subd_mesh(Scene *scene, static void create_subd_mesh(Scene *scene,
Mesh *mesh, Mesh *mesh,
BL::Object b_ob,
BL::Mesh& b_mesh, BL::Mesh& b_mesh,
PointerRNA *cmesh, PointerRNA *cmesh,
const vector<uint>& used_shaders) const vector<uint>& used_shaders)
@ -691,8 +693,10 @@ static void create_subd_mesh(Scene *scene,
SubdParams sdparams(mesh, used_shaders[0], true, need_ptex); SubdParams sdparams(mesh, used_shaders[0], true, need_ptex);
sdparams.dicing_rate = RNA_float_get(cmesh, "dicing_rate"); sdparams.dicing_rate = RNA_float_get(cmesh, "dicing_rate");
//scene->camera->update();
//sdparams.camera = scene->camera; scene->camera->update();
sdparams.camera = scene->camera;
sdparams.objecttoworld = get_transform(b_ob.matrix_world());
/* tesselate */ /* tesselate */
DiagSplit dsplit(sdparams); DiagSplit dsplit(sdparams);
@ -805,7 +809,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
if(b_mesh) { if(b_mesh) {
if(render_layer.use_surfaces && !hide_tris) { if(render_layer.use_surfaces && !hide_tris) {
if(cmesh.data && experimental && RNA_boolean_get(&cmesh, "use_subdivision")) if(cmesh.data && experimental && RNA_boolean_get(&cmesh, "use_subdivision"))
create_subd_mesh(scene, mesh, b_mesh, &cmesh, used_shaders); create_subd_mesh(scene, mesh, b_ob, b_mesh, &cmesh, used_shaders);
else else
create_mesh(scene, mesh, b_mesh, used_shaders); create_mesh(scene, mesh, b_mesh, used_shaders);

@ -158,12 +158,15 @@ void Camera::update()
/* ndc to raster */ /* ndc to raster */
Transform ndctoraster = transform_scale(width, height, 1.0f) * bordertofull; Transform ndctoraster = transform_scale(width, height, 1.0f) * bordertofull;
Transform full_ndctoraster = transform_scale(full_width, full_height, 1.0f) * bordertofull;
/* raster to screen */ /* raster to screen */
Transform screentondc = fulltoborder * transform_from_viewplane(viewplane); Transform screentondc = fulltoborder * transform_from_viewplane(viewplane);
Transform screentoraster = ndctoraster * screentondc; Transform screentoraster = ndctoraster * screentondc;
Transform rastertoscreen = transform_inverse(screentoraster); Transform rastertoscreen = transform_inverse(screentoraster);
Transform full_screentoraster = full_ndctoraster * screentondc;
Transform full_rastertoscreen = transform_inverse(full_screentoraster);
/* screen to camera */ /* screen to camera */
Transform cameratoscreen; Transform cameratoscreen;
@ -177,6 +180,7 @@ void Camera::update()
Transform screentocamera = transform_inverse(cameratoscreen); Transform screentocamera = transform_inverse(cameratoscreen);
rastertocamera = screentocamera * rastertoscreen; rastertocamera = screentocamera * rastertoscreen;
Transform full_rastertocamera = screentocamera * full_rastertoscreen;
cameratoraster = screentoraster * cameratoscreen; cameratoraster = screentoraster * cameratoscreen;
cameratoworld = matrix; cameratoworld = matrix;
@ -196,12 +200,18 @@ void Camera::update()
if(type == CAMERA_ORTHOGRAPHIC) { if(type == CAMERA_ORTHOGRAPHIC) {
dx = transform_direction(&rastertocamera, make_float3(1, 0, 0)); dx = transform_direction(&rastertocamera, make_float3(1, 0, 0));
dy = transform_direction(&rastertocamera, make_float3(0, 1, 0)); dy = transform_direction(&rastertocamera, make_float3(0, 1, 0));
full_dx = transform_direction(&full_rastertocamera, make_float3(1, 0, 0));
full_dy = transform_direction(&full_rastertocamera, make_float3(0, 1, 0));
} }
else if(type == CAMERA_PERSPECTIVE) { else if(type == CAMERA_PERSPECTIVE) {
dx = transform_perspective(&rastertocamera, make_float3(1, 0, 0)) - dx = transform_perspective(&rastertocamera, make_float3(1, 0, 0)) -
transform_perspective(&rastertocamera, make_float3(0, 0, 0)); transform_perspective(&rastertocamera, make_float3(0, 0, 0));
dy = transform_perspective(&rastertocamera, make_float3(0, 1, 0)) - dy = transform_perspective(&rastertocamera, make_float3(0, 1, 0)) -
transform_perspective(&rastertocamera, make_float3(0, 0, 0)); transform_perspective(&rastertocamera, make_float3(0, 0, 0));
full_dx = transform_perspective(&full_rastertocamera, make_float3(1, 0, 0)) -
transform_perspective(&full_rastertocamera, make_float3(0, 0, 0));
full_dy = transform_perspective(&full_rastertocamera, make_float3(0, 1, 0)) -
transform_perspective(&full_rastertocamera, make_float3(0, 0, 0));
} }
else { else {
dx = make_float3(0.0f, 0.0f, 0.0f); dx = make_float3(0.0f, 0.0f, 0.0f);
@ -210,6 +220,8 @@ void Camera::update()
dx = transform_direction(&cameratoworld, dx); dx = transform_direction(&cameratoworld, dx);
dy = transform_direction(&cameratoworld, dy); dy = transform_direction(&cameratoworld, dy);
full_dx = transform_direction(&cameratoworld, full_dx);
full_dy = transform_direction(&cameratoworld, full_dy);
/* TODO(sergey): Support other types of camera. */ /* TODO(sergey): Support other types of camera. */
if(type == CAMERA_PERSPECTIVE) { if(type == CAMERA_PERSPECTIVE) {
@ -539,4 +551,32 @@ BoundBox Camera::viewplane_bounds_get()
return bounds; return bounds;
} }
float Camera::world_to_raster_size(float3 P)
{
if(type == CAMERA_ORTHOGRAPHIC) {
return min(len(full_dx), len(full_dy));
}
else if(type == CAMERA_PERSPECTIVE) {
/* Calculate as if point is directly ahead of the camera. */
float3 raster = make_float3(0.5f*width, 0.5f*height, 0.0f);
float3 Pcamera = transform_perspective(&rastertocamera, raster);
/* dDdx */
float3 Ddiff = transform_direction(&cameratoworld, Pcamera);
float3 dx = len_squared(full_dx) < len_squared(full_dy) ? full_dx : full_dy;
float3 dDdx = normalize(Ddiff + dx) - normalize(Ddiff);
/* dPdx */
float dist = len(transform_point(&worldtocamera, P));
float3 D = normalize(Ddiff);
return len(dist*dDdx - dot(dist*dDdx, D)*D);
}
else {
// TODO(mai): implement for CAMERA_PANORAMA
assert(!"pixel width calculation for panoramic projection not implemented yet");
}
return 1.0f;
}
CCL_NAMESPACE_END CCL_NAMESPACE_END

@ -120,6 +120,8 @@ public:
int width, height; int width, height;
int resolution; int resolution;
BoundBox2D viewplane; BoundBox2D viewplane;
/* width and height change during preview, so we need these for calculating dice rates. */
int full_width, full_height;
/* border */ /* border */
BoundBox2D border; BoundBox2D border;
@ -151,6 +153,9 @@ public:
float3 dx; float3 dx;
float3 dy; float3 dy;
float3 full_dx;
float3 full_dy;
/* update */ /* update */
bool need_update; bool need_update;
bool need_device_update; bool need_device_update;
@ -176,6 +181,9 @@ public:
/* Public utility functions. */ /* Public utility functions. */
BoundBox viewplane_bounds_get(); BoundBox viewplane_bounds_get();
/* Calculates the width of a pixel at point in world space. */
float world_to_raster_size(float3 P);
private: private:
/* Private utility functions. */ /* Private utility functions. */
float3 transform_raster_to_world(float raster_x, float raster_y); float3 transform_raster_to_world(float raster_x, float raster_y);

@ -305,7 +305,12 @@ void QuadDice::dice(SubPatch& sub, EdgeFactors& ef)
int Mu = max(ef.tu0, ef.tu1); int Mu = max(ef.tu0, ef.tu1);
int Mv = max(ef.tv0, ef.tv1); int Mv = max(ef.tv0, ef.tv1);
#if 0 /* Doesnt work very well, especially at grazing angles. */
float S = scale_factor(sub, ef, Mu, Mv); float S = scale_factor(sub, ef, Mu, Mv);
#else
float S = 1.0f;
#endif
Mu = max((int)ceil(S*Mu), 2); // XXX handle 0 & 1? Mu = max((int)ceil(S*Mu), 2); // XXX handle 0 & 1?
Mv = max((int)ceil(S*Mv), 2); // XXX handle 0 & 1? Mv = max((int)ceil(S*Mv), 2); // XXX handle 0 & 1?

@ -41,6 +41,7 @@ struct SubdParams {
int split_threshold; int split_threshold;
float dicing_rate; float dicing_rate;
Camera *camera; Camera *camera;
Transform objecttoworld;
SubdParams(Mesh *mesh_, int shader_, bool smooth_ = true, bool ptex_ = false) SubdParams(Mesh *mesh_, int shader_, bool smooth_ = true, bool ptex_ = false)
{ {

@ -46,13 +46,13 @@ void DiagSplit::dispatch(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors&
edgefactors_triangle.push_back(ef); edgefactors_triangle.push_back(ef);
} }
float3 DiagSplit::project(Patch *patch, float2 uv) float3 DiagSplit::to_world(Patch *patch, float2 uv)
{ {
float3 P; float3 P;
patch->eval(&P, NULL, NULL, uv.x, uv.y); patch->eval(&P, NULL, NULL, uv.x, uv.y);
if(params.camera) if(params.camera)
P = transform_perspective(&params.camera->worldtoraster, P); P = transform_point(&params.objecttoworld, P);
return P; return P;
} }
@ -66,10 +66,21 @@ int DiagSplit::T(Patch *patch, float2 Pstart, float2 Pend)
for(int i = 0; i < params.test_steps; i++) { for(int i = 0; i < params.test_steps; i++) {
float t = i/(float)(params.test_steps-1); float t = i/(float)(params.test_steps-1);
float3 P = project(patch, Pstart + t*(Pend - Pstart)); float3 P = to_world(patch, Pstart + t*(Pend - Pstart));
if(i > 0) { if(i > 0) {
float L = len(P - Plast); float L;
if(!params.camera) {
L = len(P - Plast);
}
else {
Camera* cam = params.camera;
float pixel_width = cam->world_to_raster_size((P + Plast) * 0.5f);
L = len(P - Plast) / pixel_width;
}
Lsum += L; Lsum += L;
Lmax = max(L, Lmax); Lmax = max(L, Lmax);
} }
@ -103,6 +114,16 @@ void DiagSplit::partition_edge(Patch *patch, float2 *P, int *t0, int *t1, float2
void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int depth) void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef, int depth)
{ {
if(depth > 32) {
/* We should never get here, but just in case end recursion safely. */
ef.tu = 1;
ef.tv = 1;
ef.tw = 1;
dispatch(sub, ef);
return;
}
assert(ef.tu == T(sub.patch, sub.Pv, sub.Pw)); assert(ef.tu == T(sub.patch, sub.Pv, sub.Pw));
assert(ef.tv == T(sub.patch, sub.Pw, sub.Pu)); assert(ef.tv == T(sub.patch, sub.Pw, sub.Pu));
assert(ef.tw == T(sub.patch, sub.Pu, sub.Pv)); assert(ef.tw == T(sub.patch, sub.Pu, sub.Pv));
@ -187,7 +208,25 @@ void DiagSplit::split(TriangleDice::SubPatch& sub, TriangleDice::EdgeFactors& ef
void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int depth) void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int depth)
{ {
if((ef.tu0 == DSPLIT_NON_UNIFORM || ef.tu1 == DSPLIT_NON_UNIFORM)) { if(depth > 32) {
/* We should never get here, but just in case end recursion safely. */
ef.tu0 = 1;
ef.tu1 = 1;
ef.tv0 = 1;
ef.tv1 = 1;
dispatch(sub, ef);
return;
}
bool split_u = (ef.tu0 == DSPLIT_NON_UNIFORM || ef.tu1 == DSPLIT_NON_UNIFORM);
bool split_v = (ef.tv0 == DSPLIT_NON_UNIFORM || ef.tv1 == DSPLIT_NON_UNIFORM);
if(split_u && split_v) {
split_u = depth % 2;
}
if(split_u) {
/* partition edges */ /* partition edges */
QuadDice::EdgeFactors ef0, ef1; QuadDice::EdgeFactors ef0, ef1;
float2 Pu0, Pu1; float2 Pu0, Pu1;
@ -212,7 +251,7 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de
split(sub0, ef0, depth+1); split(sub0, ef0, depth+1);
split(sub1, ef1, depth+1); split(sub1, ef1, depth+1);
} }
else if(ef.tv0 == DSPLIT_NON_UNIFORM || ef.tv1 == DSPLIT_NON_UNIFORM) { else if(split_v) {
/* partition edges */ /* partition edges */
QuadDice::EdgeFactors ef0, ef1; QuadDice::EdgeFactors ef0, ef1;
float2 Pv0, Pv1; float2 Pv0, Pv1;
@ -237,8 +276,9 @@ void DiagSplit::split(QuadDice::SubPatch& sub, QuadDice::EdgeFactors& ef, int de
split(sub0, ef0, depth+1); split(sub0, ef0, depth+1);
split(sub1, ef1, depth+1); split(sub1, ef1, depth+1);
} }
else else {
dispatch(sub, ef); dispatch(sub, ef);
}
} }
void DiagSplit::split_triangle(Patch *patch) void DiagSplit::split_triangle(Patch *patch)

@ -45,7 +45,7 @@ public:
DiagSplit(const SubdParams& params); DiagSplit(const SubdParams& params);
float3 project(Patch *patch, float2 uv); float3 to_world(Patch *patch, float2 uv);
int T(Patch *patch, float2 Pstart, float2 Pend); int T(Patch *patch, float2 Pstart, float2 Pend);
void partition_edge(Patch *patch, float2 *P, int *t0, int *t1, void partition_edge(Patch *patch, float2 *P, int *t0, int *t1,
float2 Pstart, float2 Pend, int t); float2 Pstart, float2 Pend, int t);