398 lines
11 KiB
Rust
398 lines
11 KiB
Rust
use crate::core::geometry::{
|
|
Normal3f, Point2f, Point3f, Vector2f, Vector3f, VectorLike, spherical_phi, spherical_theta,
|
|
};
|
|
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
|
use crate::core::pbrt::Float;
|
|
use crate::core::pbrt::{INV_2_PI, INV_PI, PI};
|
|
use crate::images::WrapMode;
|
|
use crate::spectra::color::ColorEncoding;
|
|
use crate::spectra::{
|
|
RGB, RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
|
SampledWavelengths, Spectrum, SpectrumTrait,
|
|
};
|
|
use crate::textures::*;
|
|
use crate::utils::Transform;
|
|
use crate::utils::math::square;
|
|
|
|
#[repr(C)]
|
|
pub struct TexCoord2D {
|
|
pub st: Point2f,
|
|
pub dsdx: Float,
|
|
pub dsdy: Float,
|
|
pub dtdx: Float,
|
|
pub dtdy: Float,
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Clone, Debug)]
|
|
pub enum TextureMapping2D {
|
|
UV(UVMapping),
|
|
Spherical(SphericalMapping),
|
|
Cylindrical(CylindricalMapping),
|
|
Planar(PlanarMapping),
|
|
}
|
|
|
|
impl TextureMapping2D {
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
match self {
|
|
TextureMapping2D::UV(t) => t.map(ctx),
|
|
TextureMapping2D::Spherical(t) => t.map(ctx),
|
|
TextureMapping2D::Cylindrical(t) => t.map(ctx),
|
|
TextureMapping2D::Planar(t) => t.map(ctx),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct UVMapping {
|
|
su: Float,
|
|
sv: Float,
|
|
du: Float,
|
|
dv: Float,
|
|
}
|
|
|
|
impl Default for UVMapping {
|
|
fn default() -> Self {
|
|
Self {
|
|
su: 1.0,
|
|
sv: 1.0,
|
|
du: 0.0,
|
|
dv: 0.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl UVMapping {
|
|
pub fn new(su: Float, sv: Float, du: Float, dv: Float) -> Self {
|
|
Self { su, sv, du, dv }
|
|
}
|
|
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
let dsdx = self.su * ctx.dudx;
|
|
let dsdy = self.su * ctx.dudy;
|
|
let dtdx = self.sv * ctx.dvdx;
|
|
let dtdy = self.sv * ctx.dvdy;
|
|
let st = Point2f::new(self.su * ctx.uv[0] + self.du, self.sv * ctx.uv[1] * self.dv);
|
|
TexCoord2D {
|
|
st,
|
|
dsdx,
|
|
dsdy,
|
|
dtdx,
|
|
dtdy,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SphericalMapping {
|
|
texture_from_render: Transform,
|
|
}
|
|
|
|
impl SphericalMapping {
|
|
pub fn new(texture_from_render: &Transform) -> Self {
|
|
Self {
|
|
texture_from_render: *texture_from_render,
|
|
}
|
|
}
|
|
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
|
let x2y2 = square(pt.x()) + square(pt.y());
|
|
let sqrtx2y2 = x2y2.sqrt();
|
|
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
|
|
let dtdp = 1. / (PI * (x2y2 * square(pt.z())))
|
|
* Vector3f::new(
|
|
pt.x() * pt.z() / sqrtx2y2,
|
|
pt.y() * pt.z() / sqrtx2y2,
|
|
-sqrtx2y2,
|
|
);
|
|
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
|
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
|
let dsdx = dsdp.dot(dpdx);
|
|
let dsdy = dsdp.dot(dpdy);
|
|
let dtdx = dtdp.dot(dpdx);
|
|
let dtdy = dtdp.dot(dpdy);
|
|
let vec = (pt - Point3f::default()).normalize();
|
|
let st = Point2f::new(spherical_theta(vec) * INV_PI, spherical_phi(vec) * INV_2_PI);
|
|
TexCoord2D {
|
|
st,
|
|
dsdx,
|
|
dsdy,
|
|
dtdx,
|
|
dtdy,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct CylindricalMapping {
|
|
texture_from_render: Transform,
|
|
}
|
|
|
|
impl CylindricalMapping {
|
|
pub fn new(texture_from_render: &Transform) -> Self {
|
|
Self {
|
|
texture_from_render: *texture_from_render,
|
|
}
|
|
}
|
|
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
let pt = self.texture_from_render.apply_to_point(ctx.p);
|
|
let x2y2 = square(pt.x()) + square(pt.y());
|
|
let dsdp = Vector3f::new(-pt.y(), pt.x(), 0.) / (2. * PI * x2y2);
|
|
let dtdp = Vector3f::new(1., 0., 0.);
|
|
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
|
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
|
let dsdx = dsdp.dot(dpdx);
|
|
let dsdy = dsdp.dot(dpdy);
|
|
let dtdx = dtdp.dot(dpdx);
|
|
let dtdy = dtdp.dot(dpdy);
|
|
let st = Point2f::new((PI * pt.y().atan2(pt.x())) * INV_2_PI, pt.z());
|
|
TexCoord2D {
|
|
st,
|
|
dsdx,
|
|
dsdy,
|
|
dtdx,
|
|
dtdy,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct PlanarMapping {
|
|
texture_from_render: Transform,
|
|
vs: Vector3f,
|
|
vt: Vector3f,
|
|
ds: Float,
|
|
dt: Float,
|
|
}
|
|
|
|
impl PlanarMapping {
|
|
pub fn new(
|
|
texture_from_render: &Transform,
|
|
vs: Vector3f,
|
|
vt: Vector3f,
|
|
ds: Float,
|
|
dt: Float,
|
|
) -> Self {
|
|
Self {
|
|
texture_from_render: *texture_from_render,
|
|
vs,
|
|
vt,
|
|
ds,
|
|
dt,
|
|
}
|
|
}
|
|
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord2D {
|
|
let vec: Vector3f = self.texture_from_render.apply_to_point(ctx.p).into();
|
|
let dpdx = self.texture_from_render.apply_to_vector(ctx.dpdx);
|
|
let dpdy = self.texture_from_render.apply_to_vector(ctx.dpdy);
|
|
let dsdx = self.vs.dot(dpdx);
|
|
let dsdy = self.vs.dot(dpdy);
|
|
let dtdx = self.vt.dot(dpdx);
|
|
let dtdy = self.vt.dot(dpdy);
|
|
let st = Point2f::new(self.ds + vec.dot(self.vs), self.dt + vec.dot(self.vt));
|
|
TexCoord2D {
|
|
st,
|
|
dsdx,
|
|
dsdy,
|
|
dtdx,
|
|
dtdy,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct TexCoord3D {
|
|
pub p: Point3f,
|
|
pub dpdx: Vector3f,
|
|
pub dpdy: Vector3f,
|
|
}
|
|
|
|
pub trait TextureMapping3DTrait {
|
|
fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum TextureMapping3D {
|
|
PointTransform(PointTransformMapping),
|
|
}
|
|
|
|
impl TextureMapping3D {
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D {
|
|
match self {
|
|
TextureMapping3D::PointTransform(t) => t.map(ctx),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct PointTransformMapping {
|
|
texture_from_render: Transform,
|
|
}
|
|
|
|
impl PointTransformMapping {
|
|
pub fn new(texture_from_render: &Transform) -> Self {
|
|
Self {
|
|
texture_from_render: *texture_from_render,
|
|
}
|
|
}
|
|
|
|
pub fn map(&self, ctx: &TextureEvalContext) -> TexCoord3D {
|
|
TexCoord3D {
|
|
p: self.texture_from_render.apply_to_point(ctx.p),
|
|
dpdx: self.texture_from_render.apply_to_vector(ctx.dpdx),
|
|
dpdy: self.texture_from_render.apply_to_vector(ctx.dpdy),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default, Debug)]
|
|
pub struct TextureEvalContext {
|
|
pub p: Point3f,
|
|
pub dpdx: Vector3f,
|
|
pub dpdy: Vector3f,
|
|
pub n: Normal3f,
|
|
pub uv: Point2f,
|
|
pub dudx: Float,
|
|
pub dudy: Float,
|
|
pub dvdx: Float,
|
|
pub dvdy: Float,
|
|
pub face_index: usize,
|
|
}
|
|
|
|
impl TextureEvalContext {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
p: Point3f,
|
|
dpdx: Vector3f,
|
|
dpdy: Vector3f,
|
|
n: Normal3f,
|
|
uv: Point2f,
|
|
dudx: Float,
|
|
dudy: Float,
|
|
dvdx: Float,
|
|
dvdy: Float,
|
|
face_index: usize,
|
|
) -> Self {
|
|
Self {
|
|
p,
|
|
dpdx,
|
|
dpdy,
|
|
n,
|
|
uv,
|
|
dudx,
|
|
dudy,
|
|
dvdx,
|
|
dvdy,
|
|
face_index,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&SurfaceInteraction> for TextureEvalContext {
|
|
fn from(si: &SurfaceInteraction) -> Self {
|
|
Self {
|
|
p: si.p(),
|
|
dpdx: si.dpdx,
|
|
dpdy: si.dpdy,
|
|
n: si.common.n,
|
|
uv: si.uv,
|
|
dudx: si.dudx,
|
|
dudy: si.dudy,
|
|
dvdx: si.dvdx,
|
|
dvdy: si.dvdy,
|
|
face_index: si.face_index,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Interaction> for TextureEvalContext {
|
|
fn from(intr: &Interaction) -> Self {
|
|
match intr {
|
|
Interaction::Surface(si) => TextureEvalContext::from(si),
|
|
Interaction::Medium(mi) => TextureEvalContext {
|
|
p: mi.p(),
|
|
..Default::default()
|
|
},
|
|
|
|
Interaction::Simple(si) => TextureEvalContext {
|
|
p: si.p(),
|
|
..Default::default()
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub enum GPUFloatTexture {
|
|
Constant(FloatConstantTexture),
|
|
DirectionMix(FloatDirectionMixTexture),
|
|
Scaled(FloatScaledTexture),
|
|
Bilerp(FloatBilerpTexture),
|
|
Checkerboard(FloatCheckerboardTexture),
|
|
Dots(FloatDotsTexture),
|
|
FBm(FBmTexture),
|
|
Windy(WindyTexture),
|
|
Wrinkled(WrinkledTexture),
|
|
Ptex(GPUFloatPtexTexture),
|
|
Image(GPUFloatImageTexture),
|
|
Mix(GPUFloatMixTexture),
|
|
}
|
|
|
|
impl GPUFloatTexture {
|
|
pub fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
|
match self {
|
|
GPUFloatTexture::Constant(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::DirectionMix(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Scaled(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Bilerp(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Checkerboard(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Dots(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::FBm(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Windy(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Wrinkle(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Ptex(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Image(t) => t.evaluate(ctx),
|
|
GPUFloatTexture::Mix(t) => t.evaluate(ctx),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum SpectrumType {
|
|
Illuminant,
|
|
Albedo,
|
|
Unbounded,
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub enum GPUSpectrumTexture {
|
|
Constant(SpectrumConstantTexture),
|
|
Bilerp(SpectrumBilerpTexture),
|
|
Checkerboard(SpectrumCheckerboardTexture),
|
|
Marble(MarbleTexture),
|
|
DirectionMix(SpectrumDirectionMixTexture),
|
|
Dots(SpectrumDotsTexture),
|
|
Scaled(SpectrumScaledTexture),
|
|
Image(GPUSpectrumImageTexture),
|
|
Ptex(GPUSpectrumPtexTexture),
|
|
Mix(GPUSpectrumMixTexture),
|
|
}
|
|
|
|
impl GPUSpectrumTexture {
|
|
pub fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> Float {
|
|
match self {
|
|
GPUSpectrumTexture::Constant(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Bilerp(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Checkerboard(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Marble(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::DirectionMix(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Dots(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Scaled(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Ptex(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Image(t) => t.evaluate(ctx, lambda),
|
|
GPUSpectrumTexture::Mix(t) => t.evaluate(ctx, lambda),
|
|
}
|
|
}
|
|
}
|