forked from enlightenment/efl
Evas filters: Implement bump maps
These bump maps implement two light effects at once: - 3D Shadows - Specular lights The specular light is activated by a flag. Another flag enables compensation for darkening/whitening of horizontal surfaces. NOTE: This implementation is VERY SLOW. It uses double values and divisions all over the place. It might be possible to optimize by computing two LUTs before running the algorithm.
This commit is contained in:
parent
40c7f90bdc
commit
d7ed901ffd
|
@ -0,0 +1,424 @@
|
||||||
|
/**
|
||||||
|
* @brief Simple bump maps algorithms for the software engine
|
||||||
|
* @file evas_filter_bump.c
|
||||||
|
* @author Jean-Philippe ANDRE <jpeg@videolan.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "evas_common_private.h"
|
||||||
|
#include "evas_private.h"
|
||||||
|
#include "evas_filter.h"
|
||||||
|
#include "evas_filter_private.h"
|
||||||
|
#include "evas_blend_private.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#undef ENFN
|
||||||
|
#undef ENDT
|
||||||
|
#define ENFN cmd->ctx->evas->engine.func
|
||||||
|
#define ENDT cmd->ctx->evas->engine.data.output
|
||||||
|
#define CLAMP(a,b,c) MIN(MAX((b),(a)),(c))
|
||||||
|
|
||||||
|
#define DEFAULT_ZANGLE 45.f
|
||||||
|
|
||||||
|
static Eina_Bool _bump_map_cpu_alpha_alpha(Evas_Filter_Command *cmd);
|
||||||
|
static Eina_Bool _bump_map_cpu_alpha_rgba(Evas_Filter_Command *cmd);
|
||||||
|
static Eina_Bool _bump_map_cpu_rgba_rgba(Evas_Filter_Command *cmd);
|
||||||
|
|
||||||
|
Evas_Filter_Apply_Func
|
||||||
|
evas_filter_bump_map_cpu_func_get(Evas_Filter_Command *cmd)
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
|
||||||
|
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->input != cmd->output, NULL);
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->alpha_only, NULL);
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((!cmd->output->alpha_only)
|
||||||
|
|| cmd->input->alpha_only, NULL);
|
||||||
|
|
||||||
|
w = cmd->input->w;
|
||||||
|
h = cmd->input->h;
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->w == w, NULL);
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->output->h == h, NULL);
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->w == w, NULL);
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mask->h == h, NULL);
|
||||||
|
|
||||||
|
if (cmd->input->alpha_only)
|
||||||
|
{
|
||||||
|
if (cmd->output->alpha_only)
|
||||||
|
return _bump_map_cpu_alpha_alpha;
|
||||||
|
else
|
||||||
|
return _bump_map_cpu_alpha_rgba;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return _bump_map_cpu_rgba_rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_phong_alpha_generate(DATA8 *phong, DATA8 dark, DATA8 color, DATA8 white,
|
||||||
|
float sf)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
// FIXME: Flat surfaces should be of color COLOR when compensate is set
|
||||||
|
// FIXME: Include white in the computation for specular light
|
||||||
|
(void) white;
|
||||||
|
(void) sf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
float3 lightDir = light.position - pos3D; //3D position in space of the surface
|
||||||
|
float distance = length( lightDir );
|
||||||
|
lightDir = lightDir / distance; // = normalize( lightDir );
|
||||||
|
distance = distance * distance; //This line may be optimised using Inverse square root
|
||||||
|
|
||||||
|
//Intensity of the diffuse light. Saturate to keep within the 0-1 range.
|
||||||
|
float NdotL = dot( normal, lightDir );
|
||||||
|
float intensity = saturate( NdotL );
|
||||||
|
|
||||||
|
// Calculate the diffuse light factoring in light color, power and the attenuation
|
||||||
|
OUT.Diffuse = intensity * light.diffuseColor * light.diffusePower / distance;
|
||||||
|
|
||||||
|
//Calculate the half vector between the light vector and the view vector.
|
||||||
|
//This is faster than calculating the actual reflective vector.
|
||||||
|
float3 H = normalize( lightDir + viewDir );
|
||||||
|
|
||||||
|
//Intensity of the specular light
|
||||||
|
float NdotH = dot( normal, H );
|
||||||
|
intensity = pow( saturate( NdotH ), specularHardness );
|
||||||
|
|
||||||
|
//Sum up the specular light factoring
|
||||||
|
OUT.Specular = intensity * light.specularColor * light.specularPower / distance;
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (y = 0; y < 256; y++)
|
||||||
|
for (x = 0; x < 256; x++)
|
||||||
|
{
|
||||||
|
float dx = (127.5 - x);
|
||||||
|
float dy = (127.5 - y);
|
||||||
|
float dist = sqrt(dx*dx + dy*dy) * 2.;
|
||||||
|
int diff = dark + MAX(255 - dist, 0) * (color - dark) / 255;
|
||||||
|
int spec = 0; // TODO
|
||||||
|
phong[x + (y << 8)] = MIN(MAX(diff + spec, 0), 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_bump_map_cpu_alpha_alpha(Evas_Filter_Command *cmd)
|
||||||
|
{
|
||||||
|
DATA8 *src, *map, *dst, *map_y1, *map_y2;
|
||||||
|
DATA8 dark, color, white;
|
||||||
|
DATA8 *phong;
|
||||||
|
int x, y, w, h, lx, ly;
|
||||||
|
float xyangle, zangle, sf, lxy;
|
||||||
|
|
||||||
|
w = cmd->input->w;
|
||||||
|
h = cmd->input->h;
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(w > 2 && h > 2, EINA_FALSE);
|
||||||
|
|
||||||
|
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
|
||||||
|
map = ((RGBA_Image *) cmd->mask->backing)->mask.data;
|
||||||
|
dst = ((RGBA_Image *) cmd->output->backing)->mask.data;
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(map, EINA_FALSE);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
|
||||||
|
|
||||||
|
xyangle = cmd->bump.xyangle;
|
||||||
|
zangle = cmd->bump.zangle;
|
||||||
|
sf = cmd->bump.specular_factor;
|
||||||
|
|
||||||
|
dark = cmd->bump.dark >> 24;
|
||||||
|
white = cmd->bump.white >> 24;
|
||||||
|
color = cmd->bump.color >> 24;
|
||||||
|
|
||||||
|
// Convenience for alpha output only
|
||||||
|
if ((!dark && !white && !color) ||
|
||||||
|
(dark == 0xff && white == 0xff && color == 0xff))
|
||||||
|
{
|
||||||
|
INF("Bump colors are all 0 or 255. Using low byte instead of alpha.");
|
||||||
|
dark = cmd->bump.dark & 0xff;
|
||||||
|
white = cmd->bump.white & 0xff;
|
||||||
|
color = cmd->bump.color & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute appropriate lx, ly
|
||||||
|
if (abs(zangle) >= 90)
|
||||||
|
{
|
||||||
|
WRN("Z angle was defined as %.0f, out of range. Defaults to %.0f.",
|
||||||
|
zangle, DEFAULT_ZANGLE);
|
||||||
|
zangle = DEFAULT_ZANGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lxy = sin(abs(zangle * M_PI / 180.));
|
||||||
|
lx = (int) (40.f * (lxy + 1.0) * cos(xyangle * M_PI / 180.));
|
||||||
|
ly = (int) (40.f * (lxy + 1.0) * sin(xyangle * M_PI / 180.));
|
||||||
|
INF("Using light vector (%d,%d)", lx, ly);
|
||||||
|
|
||||||
|
// Generate light table
|
||||||
|
phong = malloc(256 * 256 * sizeof(*phong));
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(phong, EINA_FALSE);
|
||||||
|
_phong_alpha_generate(phong, dark, color, white, sf);
|
||||||
|
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
{
|
||||||
|
int gx, gy, vx, vy;
|
||||||
|
|
||||||
|
if (!y)
|
||||||
|
{
|
||||||
|
map_y1 = map;
|
||||||
|
map_y2 = map + w;
|
||||||
|
}
|
||||||
|
else if (y == (h - 1))
|
||||||
|
{
|
||||||
|
map_y1 = map - w;
|
||||||
|
map_y2 = map;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map_y1 = map - w;
|
||||||
|
map_y2 = map + w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// x = 0
|
||||||
|
gx = (map[1] - map[0]) / 2;
|
||||||
|
gy = (*map_y2 - *map_y1) / 2;
|
||||||
|
vx = gx + lx + 127;
|
||||||
|
vy = (-gy) + ly + 127;
|
||||||
|
|
||||||
|
//printf("dx,dy: %d,%d, lx,ly: %d,%d, vx,vy: %d,%d\n", gx, gy, lx, ly, vx, vy);
|
||||||
|
|
||||||
|
if ((vx & 0xFF00) || (vy & 0xFF00))
|
||||||
|
*dst = *src * dark;
|
||||||
|
else
|
||||||
|
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
|
||||||
|
|
||||||
|
dst++, src++, map_y1++, map_y2++;
|
||||||
|
for (x = 1; x < (w - 1); x++, map++, map_y1++, map_y2++, src++, dst++)
|
||||||
|
{
|
||||||
|
// note: map is one pixel left of (x,y)
|
||||||
|
|
||||||
|
if (!*src)
|
||||||
|
{
|
||||||
|
*dst = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute gradient (gx, gy). this gives us the normal vector
|
||||||
|
gx = (map[2] - map[0]) / 2;
|
||||||
|
gy = (*map_y2 - *map_y1) / 2;
|
||||||
|
|
||||||
|
// compute halfway vector between light and gradient vectors
|
||||||
|
vx = gx + lx + 127;
|
||||||
|
vy = (-gy) + ly + 127;
|
||||||
|
|
||||||
|
// take light from the phong table
|
||||||
|
if ((vx & 0xFF00) || (vy & 0xFF00))
|
||||||
|
*dst = *src * dark;
|
||||||
|
else
|
||||||
|
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// x = (w - 1)
|
||||||
|
gx = (map[1] - map[0]) / 2;
|
||||||
|
gy = (*map_y2 - *map_y1) / 2;
|
||||||
|
vx = gx + lx + 127;
|
||||||
|
vy = (-gy) + ly + 127;
|
||||||
|
|
||||||
|
if ((vx & 0xFF00) || (vy & 0xFF00))
|
||||||
|
*dst = *src * dark;
|
||||||
|
else
|
||||||
|
*dst = (*src * phong[(vy << 8) + vx]) >> 8;
|
||||||
|
|
||||||
|
map += 2;
|
||||||
|
dst++;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(phong);
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_bump_map_cpu_alpha_rgba(Evas_Filter_Command *cmd)
|
||||||
|
{
|
||||||
|
DATA8 *src, *map, *map_y1, *map_y2;
|
||||||
|
DATA32 *dst;
|
||||||
|
DATA32 dark, color, white, col;
|
||||||
|
//DATA32 *phong;
|
||||||
|
Eina_Bool compensate;
|
||||||
|
int x, y, w, h, lx, ly, lz, gz, NL, diffusion, gzlz, gz2;
|
||||||
|
double xyangle, zangle, sf, lxy, elevation;
|
||||||
|
|
||||||
|
w = cmd->input->w;
|
||||||
|
h = cmd->input->h;
|
||||||
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(w > 2 && h > 2, EINA_FALSE);
|
||||||
|
|
||||||
|
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
|
||||||
|
map = ((RGBA_Image *) cmd->mask->backing)->mask.data;
|
||||||
|
dst = ((RGBA_Image *) cmd->output->backing)->image.data;
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(map, EINA_FALSE);
|
||||||
|
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
|
||||||
|
|
||||||
|
xyangle = cmd->bump.xyangle;
|
||||||
|
zangle = cmd->bump.zangle;
|
||||||
|
sf = cmd->bump.specular_factor;
|
||||||
|
compensate = cmd->bump.compensate;
|
||||||
|
elevation = cmd->bump.elevation;
|
||||||
|
|
||||||
|
dark = cmd->bump.dark;
|
||||||
|
white = cmd->bump.white;
|
||||||
|
color = cmd->bump.color;
|
||||||
|
|
||||||
|
// Compute appropriate lx, ly
|
||||||
|
if (abs(zangle) >= 90)
|
||||||
|
{
|
||||||
|
WRN("Z angle was defined as %.0f, out of range. Defaults to %.0f.",
|
||||||
|
zangle, DEFAULT_ZANGLE);
|
||||||
|
zangle = DEFAULT_ZANGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lxy = 255. * cos(zangle * M_PI / 180.);
|
||||||
|
lx = (int) (lxy * cos(xyangle * M_PI / 180.));
|
||||||
|
ly = (int) (lxy * sin(xyangle * M_PI / 180.));
|
||||||
|
lz = (int) (255. * sin(zangle));
|
||||||
|
INF("Using light vector (%d,%d,%d)", lx, ly, lz);
|
||||||
|
|
||||||
|
if (elevation <= 0)
|
||||||
|
{
|
||||||
|
WRN("Invalid elevation value of %.0f, using 10 instead.", elevation);
|
||||||
|
elevation = 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gz = (6*255) / elevation;
|
||||||
|
gzlz = gz * lz;
|
||||||
|
gz2 = gz * gz;
|
||||||
|
|
||||||
|
// Generate light table
|
||||||
|
// FIXME: phong LUT not used (we need two)
|
||||||
|
//phong = malloc(256 * 256 * sizeof(*phong));
|
||||||
|
//EINA_SAFETY_ON_NULL_RETURN_VAL(phong, EINA_FALSE);
|
||||||
|
//_phong_rgba_generate(phong, 1.5, sf, 20, dark, color, white);
|
||||||
|
|
||||||
|
// FIXME: x=0 and x=w-1 are NOT implemented.
|
||||||
|
|
||||||
|
for (y = 0; y < h; y++)
|
||||||
|
{
|
||||||
|
int gx, gy;
|
||||||
|
|
||||||
|
if (!y)
|
||||||
|
{
|
||||||
|
map_y1 = map;
|
||||||
|
map_y2 = map + w;
|
||||||
|
}
|
||||||
|
else if (y == (h - 1))
|
||||||
|
{
|
||||||
|
map_y1 = map - w;
|
||||||
|
map_y2 = map;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map_y1 = map - w;
|
||||||
|
map_y2 = map + w;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < w; x++, dst++, src++, map++, map_y1++, map_y2++)
|
||||||
|
{
|
||||||
|
if (!*src) continue;
|
||||||
|
|
||||||
|
/* Color intensity is defined as:
|
||||||
|
* I = Kd*N.L*Cd + Ks*N.H*Cs
|
||||||
|
* Where Ks and Kd are 1 in this implementation
|
||||||
|
* And Cs is white, Cd is color
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Compute N.L
|
||||||
|
* N = (gx,gy,gz)
|
||||||
|
* L = (lx,ly,lz) |L| = 255
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (EINA_LIKELY(x && (x < (w - 1))))
|
||||||
|
{
|
||||||
|
gx = map[-1] + map_y1[-1] + map_y2[-1] - map[1] - map_y1[1] - map_y2[1];
|
||||||
|
gy = map_y2[-1] + map_y2[0] + map_y2[1] - map_y1[-1] - map_y1[0] - map_y1[1];
|
||||||
|
}
|
||||||
|
else if (!x)
|
||||||
|
{
|
||||||
|
gx = map[0] + map_y1[0] + map_y2[0] - map[1] - map_y1[1] - map_y2[1];
|
||||||
|
gy = map_y2[0] + map_y2[1] + map_y2[1] - map_y1[0] - map_y1[1] - map_y1[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gx = map[-1] + map_y1[-1] + map_y2[-1] - map[0] - map_y1[0] - map_y2[0];
|
||||||
|
gy = map_y2[-1] + map_y2[0] + map_y2[0] - map_y1[-1] - map_y1[0] - map_y1[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
NL = gx*lx + gy*ly + gzlz;
|
||||||
|
|
||||||
|
if (0 && NL < 0)
|
||||||
|
{
|
||||||
|
// TODO: Check this
|
||||||
|
diffusion = lz;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int g2 = gx*gx + gy*gy + gz2;
|
||||||
|
diffusion = NL / sqrt(MAX(g2, 1));
|
||||||
|
//diffusion += MAX(0, lz - diffusion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compensate)
|
||||||
|
diffusion = diffusion * 255 / lz;
|
||||||
|
|
||||||
|
diffusion = CLAMP(1, diffusion + 1, 256);
|
||||||
|
col = INTERP_256(diffusion, color, dark);
|
||||||
|
|
||||||
|
if (sf > 0)
|
||||||
|
{
|
||||||
|
/* Compute N.H
|
||||||
|
* H = (L+V) / |L+V|
|
||||||
|
* V = (0,0,255)
|
||||||
|
* L = (lx,ly,lz) |L| = 255
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIXME: All these doubles :)
|
||||||
|
|
||||||
|
int shine;
|
||||||
|
const double hnorm = sqrt(lx*lx + ly*ly + (lz+255)*(lz+255));
|
||||||
|
const double hx = (double) lx / hnorm;
|
||||||
|
const double hy = (double) ly / hnorm;
|
||||||
|
const double hz = (double) (lz+255) / hnorm;
|
||||||
|
double NHx = hx*gx / 255.0;
|
||||||
|
double NHy = hy*gy / 255.0;
|
||||||
|
double nz = sqrt(255.0*255.0 - gx*gx - gy*gy);
|
||||||
|
double NHz = (hz*nz) / 255.0;
|
||||||
|
double NH = NHx + NHy + NHz;
|
||||||
|
double specular = NH > 0 ? pow(NH, sf) : 0;
|
||||||
|
|
||||||
|
if (compensate)
|
||||||
|
{
|
||||||
|
const double basespecular = pow(hz, sf);
|
||||||
|
shine = (specular - basespecular) * 255.0 / (1.0 - basespecular);
|
||||||
|
}
|
||||||
|
else shine = specular * 255.0;
|
||||||
|
|
||||||
|
col = INTERP_256(CLAMP(1, shine + 1, 256), white, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = INTERP_256(*src + 1, col, *dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_bump_map_cpu_rgba_rgba(Evas_Filter_Command *cmd)
|
||||||
|
{
|
||||||
|
(void) cmd;
|
||||||
|
|
||||||
|
CRIT("Not implemented yet.");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
Loading…
Reference in New Issue