new bumpmapping options for the renderer

oldbump -> original
newbump -> compatible
 *new*	-> default (3tap)
 *new*	-> best quality (5tap)

the latter two have an option to apply bumpmapping in 
 viewspace - much like displacement mapping
 objectspace - default (scales with the object)
 texturespace - much like normal mapping (scales)
This commit is contained in:
M.G. Kishalmi 2011-01-29 11:56:11 +00:00
parent e7e5fa0630
commit c709524dc9
8 changed files with 478 additions and 202 deletions

@ -994,11 +994,6 @@ class TEXTURE_PT_influence(TextureSlotPanel, bpy.types.Panel):
# color is used on grayscale textures even when use_rgb_to_intensity is disabled.
col.prop(tex, "color", text="")
if isinstance(idblock, bpy.types.Material):
sub = layout.row()
sub.prop(tex, "bump_method", text="Bump Method")
sub.active = tex.use_map_normal
col = split.column()
col.prop(tex, "invert", text="Negative")
col.prop(tex, "use_stencil")
@ -1006,6 +1001,21 @@ class TEXTURE_PT_influence(TextureSlotPanel, bpy.types.Panel):
if isinstance(idblock, bpy.types.Material) or isinstance(idblock, bpy.types.World):
col.prop(tex, "default_value", text="DVar", slider=True)
if isinstance(idblock, bpy.types.Material):
row = layout.row()
row.label(text="Bump Mapping:")
row = layout.row()
# only show bump settings if activated but not for normalmap images
row.active = tex.use_map_normal and not( tex.texture.type == 'IMAGE' and tex.texture.use_normal_map )
col = row.column()
col.prop(tex, "bump_method", text="Method")
col = row.column()
col.prop(tex, "bump_objectspace", text="Space")
col.active = tex.bump_method in ('BUMP_DEFAULT', 'BUMP_BEST_QUALITY')
class TEXTURE_PT_custom_props(TextureButtonsPanel, PropertyPanel, bpy.types.Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}

@ -820,7 +820,7 @@ static void do_init_render_material(Material *ma, int r_mode, float *amb)
/* always get derivatives for these textures */
if ELEM3(mtex->tex->type, TEX_IMAGE, TEX_PLUGIN, TEX_ENVMAP) ma->texco |= TEXCO_OSA;
else if(mtex->texflag & MTEX_NEW_BUMP) ma->texco |= TEXCO_OSA;
else if(mtex->texflag & (MTEX_COMPAT_BUMP|MTEX_3TAP_BUMP|MTEX_5TAP_BUMP)) ma->texco |= TEXCO_OSA;
if(ma->texco & (TEXCO_ORCO|TEXCO_REFL|TEXCO_NORM|TEXCO_STRAND|TEXCO_STRESS)) needuv= 1;
else if(ma->texco & (TEXCO_GLOB|TEXCO_UV|TEXCO_OBJECT|TEXCO_SPEED)) needuv= 1;

@ -630,7 +630,8 @@ void default_mtex(MTex *mtex)
mtex->size[1]= 1.0;
mtex->size[2]= 1.0;
mtex->tex= 0;
mtex->texflag= MTEX_NEW_BUMP;
mtex->texflag= MTEX_3TAP_BUMP;
mtex->texflag= MTEX_BUMP_OBJECTSPACE;
mtex->colormodel= 0;
mtex->r= 1.0;
mtex->g= 0.0;

@ -9991,12 +9991,15 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
for(a=0; a<MAX_MTEX; a++) {
if(ma->mtex[a]) {
tex= ma->mtex[a]->tex;
if(!tex)
ma->mtex[a]->texflag |= MTEX_NEW_BUMP;
else {
if(!tex) {
ma->mtex[a]->texflag |= MTEX_3TAP_BUMP;
ma->mtex[a]->texflag |= MTEX_BUMP_OBJECTSPACE;
} else {
tex= (Tex*)newlibadr(fd, ma->id.lib, tex);
if(tex && tex->type == 0) /* invalid type */
ma->mtex[a]->texflag |= MTEX_NEW_BUMP;
if(tex && tex->type == 0) { /* invalid type */
ma->mtex[a]->texflag |= MTEX_3TAP_BUMP;
ma->mtex[a]->texflag |= MTEX_BUMP_OBJECTSPACE;
}
}
}
}

@ -454,7 +454,11 @@ typedef struct TexMapping {
#define MTEX_VIEWSPACE 16
#define MTEX_DUPLI_MAPTO 32
#define MTEX_OB_DUPLI_ORIG 64
#define MTEX_NEW_BUMP 128
#define MTEX_COMPAT_BUMP 128
#define MTEX_3TAP_BUMP 256
#define MTEX_5TAP_BUMP 512
#define MTEX_BUMP_OBJECTSPACE 1024
#define MTEX_BUMP_TEXTURESPACE 2048
/* blendtype */
#define MTEX_BLEND 0

@ -378,9 +378,17 @@ static void rna_def_material_mtex(BlenderRNA *brna)
static EnumPropertyItem prop_bump_method_items[] = {
{0, "BUMP_ORIGINAL", 0, "Original", ""},
{MTEX_NEW_BUMP, "BUMP_IMPROVED", 0, "Improved", ""},
{MTEX_COMPAT_BUMP, "BUMP_COMPATIBLE", 0, "Compatible", ""},
{MTEX_3TAP_BUMP, "BUMP_DEFAULT", 0, "Default", ""},
{MTEX_5TAP_BUMP, "BUMP_BEST_QUALITY", 0, "Best Quality", ""},
{0, NULL, 0, NULL, NULL}};
static EnumPropertyItem prop_bump_space_items[] = {
{0, "BUMP_VIEWSPACE", 0, "ViewSpace", ""},
{MTEX_BUMP_OBJECTSPACE, "BUMP_OBJECTSPACE", 0, "ObjectSpace", ""},
{MTEX_BUMP_TEXTURESPACE, "BUMP_TEXTURESPACE", 0, "TextureSpace", ""},
{0, NULL, 0, NULL, NULL}};
srna= RNA_def_struct(brna, "MaterialTextureSlot", "TextureSlot");
RNA_def_struct_sdna(srna, "MTex");
RNA_def_struct_ui_text(srna, "Material Texture Slot", "Texture slot for textures in a Material datablock");
@ -687,6 +695,12 @@ static void rna_def_material_mtex(BlenderRNA *brna)
RNA_def_property_enum_items(prop, prop_bump_method_items);
RNA_def_property_ui_text(prop, "Bump Method", "Method to use for bump mapping");
RNA_def_property_update(prop, 0, "rna_Material_update");
prop= RNA_def_property(srna, "bump_objectspace", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "texflag");
RNA_def_property_enum_items(prop, prop_bump_space_items);
RNA_def_property_ui_text(prop, "Bump Space", "Space to apply bump mapping in");
RNA_def_property_update(prop, 0, "rna_Material_update");
}
static void rna_def_material_colors(StructRNA *srna)

@ -48,6 +48,7 @@ if(tex->saturation != 1.0f) { \
hsv_to_rgb(_hsv[0], _hsv[1], _hsv[2], &texres->tr, &texres->tg, &texres->tb); \
} \
#define RGBTOBW(r,g,b) ( r*0.35 + g*0.45 + b*0.2 ) /* keep this in sync with gpu_shader_material.glsl:rgbtobw */
struct HaloRen;
struct ShadeInput;

@ -1607,6 +1607,7 @@ static void texco_mapping(ShadeInput* shi, Tex* tex, MTex* mtex, float* co, floa
do_2d_mapping(mtex, texvec, shi->vlr, shi->facenor, dxt, dyt);
// translate and scale
/*
texvec[0] = mtex->size[0]*(texvec[0] - 0.5f) + mtex->ofs[0] + 0.5f;
texvec[1] = mtex->size[1]*(texvec[1] - 0.5f) + mtex->ofs[1] + 0.5f;
if (shi->osatex) {
@ -1615,6 +1616,7 @@ static void texco_mapping(ShadeInput* shi, Tex* tex, MTex* mtex, float* co, floa
dyt[0] = mtex->size[0]*dyt[0];
dyt[1] = mtex->size[1]*dyt[1];
}
*/
/* problem: repeat-mirror is not a 'repeat' but 'extend' in imagetexture.c */
// TXF: bug was here, only modify texvec when repeat mode set, old code affected other modes too.
@ -1667,6 +1669,410 @@ static void texco_mapping(ShadeInput* shi, Tex* tex, MTex* mtex, float* co, floa
}
}
/* Bump code from 2.5 development cycle, has a number of bugs, but here for compatibility */
typedef struct CompatibleBump {
float nu[3], nv[3], nn[3];
float dudnu, dudnv, dvdnu, dvdnv;
int nunvdone;
} CompatibleBump;
static void compatible_bump_init(CompatibleBump *compat_bump)
{
memset(compat_bump, 0, sizeof(*compat_bump));
compat_bump->dudnu = 1.0f;
compat_bump->dvdnv = 1.0f;
}
static void compatible_bump_uv_derivs(CompatibleBump *compat_bump, ShadeInput *shi, MTex *mtex, int i)
{
// uvmapping only, calculation of normal tangent u/v partial derivatives
// (should not be here, dudnu, dudnv, dvdnu & dvdnv should probably be part of ShadeInputUV struct,
// nu/nv in ShadeInput and this calculation should then move to shadeinput.c, shade_input_set_shade_texco() func.)
// NOTE: test for shi->obr->ob here, since vlr/obr/obi can be 'fake' when called from fastshade(), another reason to move it..
// NOTE: shi->v1 is NULL when called from displace_render_vert, assigning verts in this case is not trivial because the shi quad face side is not know.
if ((mtex->texflag & MTEX_COMPAT_BUMP) && shi->obr && shi->obr->ob && shi->v1) {
if(mtex->mapto & (MAP_NORM|MAP_WARP) && !((mtex->tex->type==TEX_IMAGE) && (mtex->tex->imaflag & TEX_NORMALMAP))) {
MTFace* tf = RE_vlakren_get_tface(shi->obr, shi->vlr, i, NULL, 0);
int j1 = shi->i1, j2 = shi->i2, j3 = shi->i3;
vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3);
// compute ortho basis around normal
if(!compat_bump->nunvdone) {
// render normal is negated
compat_bump->nn[0] = -shi->vn[0];
compat_bump->nn[1] = -shi->vn[1];
compat_bump->nn[2] = -shi->vn[2];
ortho_basis_v3v3_v3(compat_bump->nu, compat_bump->nv, compat_bump->nn);
compat_bump->nunvdone= 1;
}
if (tf) {
float *uv1 = tf->uv[j1], *uv2 = tf->uv[j2], *uv3 = tf->uv[j3];
const float an[3] = {fabsf(compat_bump->nn[0]), fabsf(compat_bump->nn[1]), fabsf(compat_bump->nn[2])};
const int a1 = (an[0] > an[1] && an[0] > an[2]) ? 1 : 0;
const int a2 = (an[2] > an[0] && an[2] > an[1]) ? 1 : 2;
const float dp1_a1 = shi->v1->co[a1] - shi->v3->co[a1];
const float dp1_a2 = shi->v1->co[a2] - shi->v3->co[a2];
const float dp2_a1 = shi->v2->co[a1] - shi->v3->co[a1];
const float dp2_a2 = shi->v2->co[a2] - shi->v3->co[a2];
const float du1 = uv1[0] - uv3[0], du2 = uv2[0] - uv3[0];
const float dv1 = uv1[1] - uv3[1], dv2 = uv2[1] - uv3[1];
const float dpdu_a1 = dv2*dp1_a1 - dv1*dp2_a1;
const float dpdu_a2 = dv2*dp1_a2 - dv1*dp2_a2;
const float dpdv_a1 = du1*dp2_a1 - du2*dp1_a1;
const float dpdv_a2 = du1*dp2_a2 - du2*dp1_a2;
float d = dpdu_a1*dpdv_a2 - dpdv_a1*dpdu_a2;
float uvd = du1*dv2 - dv1*du2;
if (uvd == 0.f) uvd = 1e-5f;
if (d == 0.f) d = 1e-5f;
d = uvd / d;
compat_bump->dudnu = (dpdv_a2*compat_bump->nu[a1] - dpdv_a1*compat_bump->nu[a2])*d;
compat_bump->dvdnu = (dpdu_a1*compat_bump->nu[a2] - dpdu_a2*compat_bump->nu[a1])*d;
compat_bump->dudnv = (dpdv_a2*compat_bump->nv[a1] - dpdv_a1*compat_bump->nv[a2])*d;
compat_bump->dvdnv = (dpdu_a1*compat_bump->nv[a2] - dpdu_a2*compat_bump->nv[a1])*d;
}
}
}
}
static int compatible_bump_compute(CompatibleBump *compat_bump, ShadeInput *shi, MTex *mtex, Tex *tex, TexResult *texres, float Tnor, float *co, float *dx, float *dy, float *texvec, float *dxt, float *dyt)
{
TexResult ttexr = {0, 0, 0, 0, 0, texres->talpha, NULL}; // temp TexResult
float tco[3], texv[3], cd, ud, vd, du, dv, idu, idv;
const int fromrgb = ((tex->type == TEX_IMAGE) || ((tex->flag & TEX_COLORBAND)!=0));
const float bf = 0.04f*Tnor*mtex->norfac;
int rgbnor;
// disable internal bump eval
float* nvec = texres->nor;
texres->nor = NULL;
// du & dv estimates, constant value defaults
du = dv = 0.01f;
// compute ortho basis around normal
if(!compat_bump->nunvdone) {
// render normal is negated
negate_v3_v3(compat_bump->nn, shi->vn);
ortho_basis_v3v3_v3(compat_bump->nu, compat_bump->nv, compat_bump->nn);
compat_bump->nunvdone= 1;
}
// two methods, either constant based on main image resolution,
// (which also works without osa, though of course not always good (or even very bad) results),
// or based on tex derivative max values (osa only). Not sure which is best...
if (!shi->osatex && (tex->type == TEX_IMAGE) && tex->ima) {
// in case we have no proper derivatives, fall back to
// computing du/dv it based on image size
ImBuf* ibuf = BKE_image_get_ibuf(tex->ima, &tex->iuser);
if (ibuf) {
du = 1.f/(float)ibuf->x;
dv = 1.f/(float)ibuf->y;
}
}
else if (shi->osatex) {
// we have derivatives, can compute proper du/dv
if (tex->type == TEX_IMAGE) { // 2d image, use u & v max. of dx/dy 2d vecs
const float adx[2] = {fabsf(dx[0]), fabsf(dx[1])};
const float ady[2] = {fabsf(dy[0]), fabsf(dy[1])};
du = MAX2(adx[0], ady[0]);
dv = MAX2(adx[1], ady[1]);
}
else { // 3d procedural, estimate from all dx/dy elems
const float adx[3] = {fabsf(dx[0]), fabsf(dx[1]), fabsf(dx[2])};
const float ady[3] = {fabsf(dy[0]), fabsf(dy[1]), fabsf(dy[2])};
du = MAX3(adx[0], adx[1], adx[2]);
dv = MAX3(ady[1], ady[1], ady[2]);
}
}
// center, main return value
texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt);
rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, texres);
cd = fromrgb ? (texres->tr + texres->tg + texres->tb)*0.33333333f : texres->tin;
if (mtex->texco == TEXCO_UV) {
// for the uv case, use the same value for both du/dv,
// since individually scaling the normal derivatives makes them useless...
du = MIN2(du, dv);
idu = (du < 1e-5f) ? bf : (bf/du);
// +u val
tco[0] = co[0] + compat_bump->dudnu*du;
tco[1] = co[1] + compat_bump->dvdnu*du;
tco[2] = 0.f;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
// +v val
tco[0] = co[0] + compat_bump->dudnv*du;
tco[1] = co[1] + compat_bump->dvdnv*du;
tco[2] = 0.f;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
vd = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
}
else {
float tu[3], tv[3];
copy_v3_v3(tu, compat_bump->nu);
copy_v3_v3(tv, compat_bump->nv);
idu = (du < 1e-5f) ? bf : (bf/du);
idv = (dv < 1e-5f) ? bf : (bf/dv);
if ((mtex->texco == TEXCO_ORCO) && shi->obr && shi->obr->ob) {
mul_mat3_m4_v3(shi->obr->ob->imat, tu);
mul_mat3_m4_v3(shi->obr->ob->imat, tv);
normalize_v3(tu);
normalize_v3(tv);
}
else if (mtex->texco == TEXCO_GLOB) {
mul_mat3_m4_v3(R.viewinv, tu);
mul_mat3_m4_v3(R.viewinv, tv);
}
else if (mtex->texco == TEXCO_OBJECT && mtex->object) {
mul_mat3_m4_v3(mtex->object->imat, tu);
mul_mat3_m4_v3(mtex->object->imat, tv);
normalize_v3(tu);
normalize_v3(tv);
}
// +u val
tco[0] = co[0] + tu[0]*du;
tco[1] = co[1] + tu[1]*du;
tco[2] = co[2] + tu[2]*du;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
// +v val
tco[0] = co[0] + tv[0]*dv;
tco[1] = co[1] + tv[1]*dv;
tco[2] = co[2] + tv[2]*dv;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
vd = idv*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
}
// bumped normal
compat_bump->nu[0] += ud*compat_bump->nn[0];
compat_bump->nu[1] += ud*compat_bump->nn[1];
compat_bump->nu[2] += ud*compat_bump->nn[2];
compat_bump->nv[0] += vd*compat_bump->nn[0];
compat_bump->nv[1] += vd*compat_bump->nn[1];
compat_bump->nv[2] += vd*compat_bump->nn[2];
cross_v3_v3v3(nvec, compat_bump->nu, compat_bump->nv);
nvec[0] = -nvec[0];
nvec[1] = -nvec[1];
nvec[2] = -nvec[2];
texres->nor = nvec;
rgbnor |= TEX_NOR;
return rgbnor;
}
/* Improved bump code from later in 2.5 development cycle */
typedef struct NTapBump {
int nunvdone;
// bumpmapping
float vNacc[3]; // original surface normal minus the surface gradient of every bump map which is encountered
float vR1[3], vR2[3]; // cross products (sigma_y, original_normal), (original_normal, sigma_x)
float sgn_det; // sign of the determinant of the matrix {sigma_x, sigma_y, original_normal}
} NTapBump;
static void ntap_bump_init(NTapBump *ntap_bump)
{
memset(ntap_bump, 0, sizeof(*ntap_bump));
}
static int ntap_bump_compute(NTapBump *ntap_bump, ShadeInput *shi, MTex *mtex, Tex *tex, TexResult *texres, float Tnor, float *co, float *dx, float *dy, float *texvec, float *dxt, float *dyt)
{
TexResult ttexr = {0, 0, 0, 0, 0, texres->talpha, NULL}; // temp TexResult
const int fromrgb = ((tex->type == TEX_IMAGE) || ((tex->flag & TEX_COLORBAND)!=0));
// TODO: solve this Hscale issue more elegantly.
float Hscale = 0.1f * Tnor*mtex->norfac; // factor 0.1 proved to look like the previous bump code
if( mtex->texflag & MTEX_BUMP_TEXTURESPACE )
Hscale *= 130.0f;
// 2 channels for 2D texture and 3 for 3D textures.
const int nr_channels = (mtex->texco == TEXCO_UV)? 2 : 3;
int c, rgbnor;
float dHdx, dHdy;
// disable internal bump eval in sampler, save pointer
float *nvec = texres->nor;
texres->nor = NULL;
if(!(mtex->texflag & MTEX_5TAP_BUMP)) {
// compute height derivatives with respect to output image pixel coordinates x and y
float STll[3], STlr[3], STul[3];
float Hll, Hlr, Hul;
texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt);
for(c=0; c<nr_channels; c++) {
// dx contains the derivatives (du/dx, dv/dx)
// dy contains the derivatives (du/dy, dv/dy)
STll[c] = texvec[c];
STlr[c] = texvec[c]+dxt[c];
STul[c] = texvec[c]+dyt[c];
}
// clear unused derivatives
for(c=nr_channels; c<3; c++) {
STll[c] = 0.0f;
STlr[c] = 0.0f;
STul[c] = 0.0f;
}
// use texres for the center sample, set rgbnor
rgbnor = multitex_mtex(shi, mtex, STll, dxt, dyt, texres);
Hll = (fromrgb)? RGBTOBW(texres->tr, texres->tg, texres->tb) : texres->tin;
// use ttexr for the other 2 taps
multitex_mtex(shi, mtex, STlr, dxt, dyt, &ttexr);
Hlr = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
multitex_mtex(shi, mtex, STul, dxt, dyt, &ttexr);
Hul = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
dHdx = Hscale*(Hlr - Hll);
dHdy = Hscale*(Hul - Hll);
}
else {
/* same as above, but doing 5 taps, increasing quality at cost of speed */
float STc[3], STl[3], STr[3], STd[3], STu[3];
float Hc, Hl, Hr, Hd, Hu;
texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt);
for(c=0; c<nr_channels; c++) {
STc[c] = texvec[c];
STl[c] = texvec[c] - 0.5f*dxt[c];
STr[c] = texvec[c] + 0.5f*dxt[c];
STd[c] = texvec[c] - 0.5f*dyt[c];
STu[c] = texvec[c] + 0.5f*dyt[c];
}
// clear unused derivatives
for(c=nr_channels; c<3; c++) {
STc[c] = 0.0f;
STl[c] = 0.0f;
STr[c] = 0.0f;
STd[c] = 0.0f;
STu[c] = 0.0f;
}
// use texres for the center sample, set rgbnor
rgbnor = multitex_mtex(shi, mtex, STc, dxt, dyt, texres);
Hc = (fromrgb)? RGBTOBW(texres->tr, texres->tg, texres->tb) : texres->tin;
// use ttexr for the other taps
multitex_mtex(shi, mtex, STl, dxt, dyt, &ttexr);
Hl = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
multitex_mtex(shi, mtex, STr, dxt, dyt, &ttexr);
Hr = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
multitex_mtex(shi, mtex, STd, dxt, dyt, &ttexr);
Hd = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
multitex_mtex(shi, mtex, STu, dxt, dyt, &ttexr);
Hu = (fromrgb)? RGBTOBW(ttexr.tr, ttexr.tg, ttexr.tb) : ttexr.tin;
dHdx = Hscale*(Hr - Hl);
dHdy = Hscale*(Hu - Hd);
}
// restore pointer
texres->nor = nvec;
/* replaced newbump with code based on listing 1 and 2 of
[Mik10] Mikkelsen M. S.: Bump Mapping Unparametrized Surfaces on the GPU.
-> http://jbit.net/~sparky/sfgrad_bump/mm_sfgrad_bump.pdf */
if(!ntap_bump->nunvdone) {
// initialize normal perturbation vectors
int xyz;
float fDet, abs_fDet;
// object2view and inverted matrix
float obj2view[3][3], view2obj[3][3], tmp[4][4];
// local copies of derivatives and normal
float dPdx[3], dPdy[3], vN[3];
VECCOPY(dPdx, shi->dxco);
VECCOPY(dPdy, shi->dyco);
VECCOPY(vN, shi->vn);
if( mtex->texflag & MTEX_BUMP_OBJECTSPACE ) {
// TODO: these calculations happen for every pixel!
// -> move to shi->obi
mul_m4_m4m4(tmp, shi->obr->ob->obmat, R.viewmat);
copy_m3_m4(obj2view, tmp); // use only upper left 3x3 matrix
invert_m3_m3(view2obj, obj2view);
// generate the surface derivatives in object space
mul_m3_v3(view2obj, dPdx);
mul_m3_v3( view2obj, dPdy );
// generate the unit normal in object space
mul_transposed_m3_v3( obj2view, vN );
normalize_v3(vN);
}
cross_v3_v3v3(ntap_bump->vR1, dPdy, vN);
cross_v3_v3v3(ntap_bump->vR2, vN, dPdx);
fDet = dot_v3v3(dPdx, ntap_bump->vR1);
ntap_bump->sgn_det = (fDet < 0)? -1.0f: 1.0f;
abs_fDet = ntap_bump->sgn_det * fDet;
if( mtex->texflag & MTEX_BUMP_TEXTURESPACE ) {
// crazy hack solution that gives results similar to normal mapping - part 1
normalize_v3(ntap_bump->vR1);
normalize_v3(ntap_bump->vR2);
abs_fDet = 1.0f;
}
for(xyz=0; xyz<3; xyz++)
ntap_bump->vNacc[xyz] = abs_fDet * vN[xyz];
if( mtex->texflag & MTEX_BUMP_OBJECTSPACE ) {
// pre do transform of texres->nor by the inverse transposed of obj2view
mul_transposed_m3_v3( view2obj, ntap_bump->vNacc );
mul_transposed_m3_v3( view2obj, ntap_bump->vR1 );
mul_transposed_m3_v3( view2obj, ntap_bump->vR2 );
}
ntap_bump->nunvdone= 1;
}
if( mtex->texflag & MTEX_BUMP_TEXTURESPACE ) {
// crazy hack solution that gives results similar to normal mapping - part 2
float vec[2];
vec[0] = tex->ima->gen_x*dxt[0];
vec[1] = tex->ima->gen_y*dxt[1];
dHdx *= 1.0f/len_v2(vec);
vec[0] = tex->ima->gen_x*dyt[0];
vec[1] = tex->ima->gen_y*dyt[1];
dHdy *= 1.0f/len_v2(vec);
}
// subtract the surface gradient from vNacc
for(c=0; c<3; c++) {
float vSurfGrad_compi = ntap_bump->sgn_det * (dHdx * ntap_bump->vR1[c] + dHdy * ntap_bump->vR2[c]);
ntap_bump->vNacc[c] -= vSurfGrad_compi;
texres->nor[c] = ntap_bump->vNacc[c]; // copy
}
rgbnor |= TEX_NOR;
return rgbnor;
}
void do_material_tex(ShadeInput *shi)
{
MTex *mtex;
@ -1676,8 +2082,13 @@ void do_material_tex(ShadeInput *shi)
float fact, facm, factt, facmm, stencilTin=1.0;
float texvec[3], dxt[3], dyt[3], tempvec[3], norvec[3], warpvec[3]={0.0f, 0.0f, 0.0f}, Tnor=1.0;
int tex_nr, rgbnor= 0, warpdone=0;
float nu[3] = {0,0,0}, nv[3] = {0,0,0}, nn[3] = {0,0,0}, dudnu = 1.f, dudnv = 0.f, dvdnu = 0.f, dvdnv = 1.f; // bump mapping
int nunvdone= 0, newbump;
CompatibleBump compat_bump;
NTapBump ntap_bump;
int use_compat_bump, use_ntap_bump;
compatible_bump_init(&compat_bump);
ntap_bump_init(&ntap_bump);
if (R.r.scemode & R_NO_TEX) return;
/* here: test flag if there's a tex (todo) */
@ -1693,9 +2104,15 @@ void do_material_tex(ShadeInput *shi)
tex= mtex->tex;
if(tex==0) continue;
use_compat_bump= (mtex->texflag & MTEX_COMPAT_BUMP);
use_ntap_bump= (mtex->texflag & (MTEX_3TAP_BUMP|MTEX_5TAP_BUMP));
/* XXX texture node trees don't work for this yet */
newbump= (mtex->texflag & MTEX_NEW_BUMP) && !(tex->nodetree && tex->use_nodes);
if(tex->nodetree && tex->use_nodes) {
use_compat_bump = 0;
use_ntap_bump = 0;
}
/* which coords */
if(mtex->texco==TEXCO_ORCO) {
if(mtex->texflag & MTEX_DUPLI_MAPTO) {
@ -1769,57 +2186,7 @@ void do_material_tex(ShadeInput *shi)
dx= suv->dxuv;
dy= suv->dyuv;
// uvmapping only, calculation of normal tangent u/v partial derivatives
// (should not be here, dudnu, dudnv, dvdnu & dvdnv should probably be part of ShadeInputUV struct,
// nu/nv in ShadeInput and this calculation should then move to shadeinput.c, shade_input_set_shade_texco() func.)
// NOTE: test for shi->obr->ob here, since vlr/obr/obi can be 'fake' when called from fastshade(), another reason to move it..
// NOTE: shi->v1 is NULL when called from displace_render_vert, assigning verts in this case is not trivial because the shi quad face side is not know.
if ((mtex->texflag & MTEX_NEW_BUMP) && shi->obr && shi->obr->ob && shi->v1) {
if(mtex->mapto & (MAP_NORM|MAP_WARP) && !((tex->type==TEX_IMAGE) && (tex->imaflag & TEX_NORMALMAP))) {
MTFace* tf = RE_vlakren_get_tface(shi->obr, shi->vlr, i, NULL, 0);
int j1 = shi->i1, j2 = shi->i2, j3 = shi->i3;
vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3);
// compute ortho basis around normal
if(!nunvdone) {
// render normal is negated
nn[0] = -shi->vn[0];
nn[1] = -shi->vn[1];
nn[2] = -shi->vn[2];
ortho_basis_v3v3_v3( nu, nv,nn);
nunvdone= 1;
}
if (tf) {
float *uv1 = tf->uv[j1], *uv2 = tf->uv[j2], *uv3 = tf->uv[j3];
const float an[3] = {fabsf(nn[0]), fabsf(nn[1]), fabsf(nn[2])};
const int a1 = (an[0] > an[1] && an[0] > an[2]) ? 1 : 0;
const int a2 = (an[2] > an[0] && an[2] > an[1]) ? 1 : 2;
const float dp1_a1 = shi->v1->co[a1] - shi->v3->co[a1];
const float dp1_a2 = shi->v1->co[a2] - shi->v3->co[a2];
const float dp2_a1 = shi->v2->co[a1] - shi->v3->co[a1];
const float dp2_a2 = shi->v2->co[a2] - shi->v3->co[a2];
const float du1 = uv1[0] - uv3[0], du2 = uv2[0] - uv3[0];
const float dv1 = uv1[1] - uv3[1], dv2 = uv2[1] - uv3[1];
const float dpdu_a1 = dv2*dp1_a1 - dv1*dp2_a1;
const float dpdu_a2 = dv2*dp1_a2 - dv1*dp2_a2;
const float dpdv_a1 = du1*dp2_a1 - du2*dp1_a1;
const float dpdv_a2 = du1*dp2_a2 - du2*dp1_a2;
float d = dpdu_a1*dpdv_a2 - dpdv_a1*dpdu_a2;
float uvd = du1*dv2 - dv1*du2;
if (uvd == 0.f) uvd = 1e-5f;
if (d == 0.f) d = 1e-5f;
d = uvd / d;
dudnu = (dpdv_a2*nu[a1] - dpdv_a1*nu[a2])*d;
dvdnu = (dpdu_a1*nu[a2] - dpdu_a2*nu[a1])*d;
dudnv = (dpdv_a2*nv[a1] - dpdv_a1*nv[a2])*d;
dvdnv = (dpdu_a1*nv[a2] - dpdu_a2*nv[a1])*d;
}
}
}
compatible_bump_uv_derivs(&compat_bump, shi, mtex, i);
}
}
else if(mtex->texco==TEXCO_WINDOW) {
@ -1858,138 +2225,14 @@ void do_material_tex(ShadeInput *shi)
}
/* XXX texture node trees don't work for this yet */
if(newbump) {
// compute ortho basis around normal
if(!nunvdone) {
// render normal is negated
nn[0] = -shi->vn[0];
nn[1] = -shi->vn[1];
nn[2] = -shi->vn[2];
ortho_basis_v3v3_v3( nu, nv,nn);
nunvdone= 1;
}
if(texres.nor && !((tex->type==TEX_IMAGE) && (tex->imaflag & TEX_NORMALMAP))) {
TexResult ttexr = {0, 0, 0, 0, 0, texres.talpha, NULL}; // temp TexResult
float tco[3], texv[3], cd, ud, vd, du, dv, idu, idv;
const int fromrgb = ((tex->type == TEX_IMAGE) || ((tex->flag & TEX_COLORBAND)!=0));
const float bf = 0.04f*Tnor*stencilTin*mtex->norfac;
// disable internal bump eval
float* nvec = texres.nor;
texres.nor = NULL;
// du & dv estimates, constant value defaults
du = dv = 0.01f;
// two methods, either constant based on main image resolution,
// (which also works without osa, though of course not always good (or even very bad) results),
// or based on tex derivative max values (osa only). Not sure which is best...
if (!shi->osatex && (tex->type == TEX_IMAGE) && tex->ima) {
// in case we have no proper derivatives, fall back to
// computing du/dv it based on image size
ImBuf* ibuf = BKE_image_get_ibuf(tex->ima, &tex->iuser);
if (ibuf) {
du = 1.f/(float)ibuf->x;
dv = 1.f/(float)ibuf->y;
}
}
else if (shi->osatex) {
// we have derivatives, can compute proper du/dv
if (tex->type == TEX_IMAGE) { // 2d image, use u & v max. of dx/dy 2d vecs
const float adx[2] = {fabsf(dx[0]), fabsf(dx[1])};
const float ady[2] = {fabsf(dy[0]), fabsf(dy[1])};
du = MAX2(adx[0], ady[0]);
dv = MAX2(adx[1], ady[1]);
}
else { // 3d procedural, estimate from all dx/dy elems
const float adx[3] = {fabsf(dx[0]), fabsf(dx[1]), fabsf(dx[2])};
const float ady[3] = {fabsf(dy[0]), fabsf(dy[1]), fabsf(dy[2])};
du = MAX3(adx[0], adx[1], adx[2]);
dv = MAX3(ady[1], ady[1], ady[2]);
}
}
// center, main return value
texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt);
rgbnor = multitex_mtex(shi, mtex, texvec, dxt, dyt, &texres);
cd = fromrgb ? (texres.tr + texres.tg + texres.tb)*0.33333333f : texres.tin;
if (mtex->texco == TEXCO_UV) {
// for the uv case, use the same value for both du/dv,
// since individually scaling the normal derivatives makes them useless...
du = MIN2(du, dv);
idu = (du < 1e-5f) ? bf : (bf/du);
// +u val
tco[0] = co[0] + dudnu*du;
tco[1] = co[1] + dvdnu*du;
tco[2] = 0.f;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
// +v val
tco[0] = co[0] + dudnv*du;
tco[1] = co[1] + dvdnv*du;
tco[2] = 0.f;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
vd = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
}
else {
float tu[3] = {nu[0], nu[1], nu[2]}, tv[3] = {nv[0], nv[1], nv[2]};
idu = (du < 1e-5f) ? bf : (bf/du);
idv = (dv < 1e-5f) ? bf : (bf/dv);
if ((mtex->texco == TEXCO_ORCO) && shi->obr && shi->obr->ob) {
mul_mat3_m4_v3(shi->obr->ob->imat, tu);
mul_mat3_m4_v3(shi->obr->ob->imat, tv);
normalize_v3(tu);
normalize_v3(tv);
}
else if (mtex->texco == TEXCO_GLOB) {
mul_mat3_m4_v3(R.viewinv, tu);
mul_mat3_m4_v3(R.viewinv, tv);
}
else if (mtex->texco == TEXCO_OBJECT && mtex->object) {
mul_mat3_m4_v3(mtex->object->imat, tu);
mul_mat3_m4_v3(mtex->object->imat, tv);
normalize_v3(tu);
normalize_v3(tv);
}
// +u val
tco[0] = co[0] + tu[0]*du;
tco[1] = co[1] + tu[1]*du;
tco[2] = co[2] + tu[2]*du;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
ud = idu*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
// +v val
tco[0] = co[0] + tv[0]*dv;
tco[1] = co[1] + tv[1]*dv;
tco[2] = co[2] + tv[2]*dv;
texco_mapping(shi, tex, mtex, tco, dx, dy, texv, dxt, dyt);
multitex_mtex(shi, mtex, texv, dxt, dyt, &ttexr);
vd = idv*(cd - (fromrgb ? (ttexr.tr + ttexr.tg + ttexr.tb)*0.33333333f : ttexr.tin));
}
// bumped normal
nu[0] += ud*nn[0];
nu[1] += ud*nn[1];
nu[2] += ud*nn[2];
nv[0] += vd*nn[0];
nv[1] += vd*nn[1];
nv[2] += vd*nn[2];
cross_v3_v3v3(nvec, nu, nv);
nvec[0] = -nvec[0];
nvec[1] = -nvec[1];
nvec[2] = -nvec[2];
texres.nor = nvec;
rgbnor |= TEX_NOR;
if(texres.nor && !((tex->type==TEX_IMAGE) && (tex->imaflag & TEX_NORMALMAP))) {
if(use_compat_bump) {
rgbnor = compatible_bump_compute(&compat_bump, shi, mtex, tex,
&texres, Tnor*stencilTin, co, dx, dy, texvec, dxt, dyt);
}
else if(use_ntap_bump) {
rgbnor = ntap_bump_compute(&ntap_bump, shi, mtex, tex,
&texres, Tnor*stencilTin, co, dx, dy, texvec, dxt, dyt);
}
else {
texco_mapping(shi, tex, mtex, co, dx, dy, texvec, dxt, dyt);
@ -2184,7 +2427,7 @@ void do_material_tex(ShadeInput *shi)
}
else {
/* XXX texture node trees don't work for this yet */
if (newbump) {
if (use_compat_bump || use_ntap_bump) {
shi->vn[0] = texres.nor[0];
shi->vn[1] = texres.nor[1];
shi->vn[2] = texres.nor[2];