diff --git a/src/textures/CMakeLists.txt b/src/textures/CMakeLists.txt
index cde5a454..75afbc26 100644
--- a/src/textures/CMakeLists.txt
+++ b/src/textures/CMakeLists.txt
@@ -2,6 +2,8 @@ add_library(luisa-render-textures INTERFACE)
luisa_render_add_plugin(constant CATEGORY texture SOURCES constant.cpp)
luisa_render_add_plugin(image CATEGORY texture SOURCES image.cpp)
luisa_render_add_plugin(swizzle CATEGORY texture SOURCES swizzle.cpp)
+luisa_render_add_plugin(scale CATEGORY texture SOURCES scale.cpp)
+luisa_render_add_plugin(multiply CATEGORY texture SOURCES multiply.cpp)
luisa_render_add_plugin(checkerboard CATEGORY texture SOURCES checkerboard.cpp)
luisa_render_add_plugin(concat CATEGORY texture SOURCES concat.cpp)
@@ -13,3 +15,6 @@ set_target_properties(luisa-render-texture-sky-precompute PROPERTIES WINDOWS_EXP
# Nishita sky texture
luisa_render_add_plugin(nishitasky CATEGORY texture SOURCES nishita_sky.cpp)
target_link_libraries(luisa-render-texture-nishitasky PRIVATE luisa-render-texture-sky-precompute)
+
+# bump to normal
+luisa_render_add_plugin(bump2normal CATEGORY texture SOURCES bump2normal.cpp)
diff --git a/src/textures/bump2normal.cpp b/src/textures/bump2normal.cpp
new file mode 100644
index 00000000..8a77d3ba
--- /dev/null
+++ b/src/textures/bump2normal.cpp
@@ -0,0 +1,28 @@
+//
+// Created by mike on 4/17/24.
+//
+/**
+* # bump_image = np.power(
+ # cv.imread(material_bump, cv.IMREAD_GRAYSCALE) / 255, 2.2
+ # )
+ # h, w = bump_image.shape[:2]
+ # scale = 4
+ # bump_image = cv.resize(bump_image, (w * scale, h * scale), interpolation=cv.INTER_LANCZOS4)
+ # bump_image = cv.GaussianBlur(bump_image, (5, 5), 0)
+ # dx_image = cv.copyMakeBorder(bump_image, 0, 0, 1, 1, cv.BORDER_REPLICATE)
+ # dy_image = cv.copyMakeBorder(bump_image, 1, 1, 0, 0, cv.BORDER_REPLICATE)
+ # strength = min(w, h) / 50 * scale
+ # dx_image = np.clip(strength * (dx_image[:, 2:] - dx_image[:, :-2]), -5, 5)
+ # dy_image = np.clip(-strength * (dy_image[2:, :] - dy_image[:-2, :]), -5, 5)
+ # dx_image = cv.resize(dx_image, (w, h), interpolation=cv.INTER_AREA)
+ # dy_image = cv.resize(dy_image, (w, h), interpolation=cv.INTER_AREA)
+ # dz_image = np.ones_like(dx_image)
+ # norm = np.sqrt(dx_image**2 + dy_image**2 + dz_image**2)
+ # normal_image = np.dstack([dz_image, dy_image, dx_image])
+ # normal_image = (normal_image / norm[:, :, np.newaxis]) * 0.5 + 0.5
+ # # normal_image = cv.GaussianBlur(normal_image, (3, 3), 0)
+ # # normal_image = cv.resize(normal_image, (w, h), interpolation=cv.INTER_CUBIC)
+ # normal_image = np.uint8(np.clip(normal_image * 255, 0, 255))
+ # save_name = f"lr_exported_textures/{material_bump.split('/')[-1]}"
+ # cv.imwrite(save_name, normal_image)
+*/
\ No newline at end of file
diff --git a/src/textures/image.cpp b/src/textures/image.cpp
index baafd938..80ee8369 100644
--- a/src/textures/image.cpp
+++ b/src/textures/image.cpp
@@ -141,15 +141,17 @@ class ImageTextureInstance final : public Texture::Instance {
auto encoding = texture->encoding();
auto scale = texture->scale();
if (encoding == ImageTexture::Encoding::SRGB) {
+ auto rgb = rgba.xyz();
auto linear = ite(
- rgba <= 0.04045f,
- rgba * (1.0f / 12.92f),
- pow((rgba + 0.055f) * (1.0f / 1.055f), 2.4f));
- return make_float4(scale * linear);
+ rgb <= 0.04045f,
+ rgb * (1.0f / 12.92f),
+ pow((rgb + 0.055f) * (1.0f / 1.055f), 2.4f));
+ return make_float4(scale * linear, rgba.w);
}
if (encoding == ImageTexture::Encoding::GAMMA) {
+ auto rgb = rgba.xyz();
auto gamma = texture->gamma();
- return scale * pow(rgba, gamma);
+ return make_float4(scale * pow(rgb, gamma), rgba.w);
}
return scale * rgba;
}
diff --git a/src/textures/multiply.cpp b/src/textures/multiply.cpp
new file mode 100644
index 00000000..d3dff02e
--- /dev/null
+++ b/src/textures/multiply.cpp
@@ -0,0 +1,69 @@
+//
+// Created by mike on 4/17/24.
+//
+
+#include
+#include
+#include
+
+namespace luisa::render {
+
+class MultiplyTexture final : public Texture {
+
+private:
+ Texture *_a;
+ Texture *_b;
+
+public:
+ MultiplyTexture(Scene *scene, const SceneNodeDesc *desc) noexcept
+ : Texture{scene, desc},
+ _a{scene->load_texture(desc->property_node("a"))},
+ _b{scene->load_texture(desc->property_node("b"))} {}
+
+ [[nodiscard]] auto a() const noexcept { return _a; }
+ [[nodiscard]] auto b() const noexcept { return _b; }
+ [[nodiscard]] bool is_black() const noexcept override { return _a->is_black() || _b->is_black(); }
+ [[nodiscard]] bool is_constant() const noexcept override { return is_black() || (_a->is_constant() && _b->is_constant()); }
+ [[nodiscard]] luisa::optional evaluate_static() const noexcept override {
+ if (auto a = _a->evaluate_static(),
+ b = _b->evaluate_static();
+ a && b) {
+ return a.value() * b.value();
+ }
+ return nullopt;
+ }
+ [[nodiscard]] luisa::string_view impl_type() const noexcept override { return LUISA_RENDER_PLUGIN_NAME; }
+ [[nodiscard]] uint channels() const noexcept override { return std::min(_a->channels(), _b->channels()); }
+ [[nodiscard]] luisa::unique_ptr build(
+ Pipeline &pipeline, CommandBuffer &command_buffer) const noexcept override;
+};
+
+class MultiplyTextureInstance final : public Texture::Instance {
+
+private:
+ const Texture::Instance *_a;
+ const Texture::Instance *_b;
+
+public:
+ MultiplyTextureInstance(const Pipeline &pipeline,
+ const Texture *node,
+ const Texture::Instance *a,
+ const Texture::Instance *b) noexcept
+ : Texture::Instance{pipeline, node}, _a{a}, _b{b} {}
+ [[nodiscard]] Float4 evaluate(const Interaction &it,
+ const SampledWavelengths &swl,
+ Expr time) const noexcept override {
+ return _a->evaluate(it, swl, time) * _b->evaluate(it, swl, time);
+ }
+};
+
+luisa::unique_ptr MultiplyTexture::build(
+ Pipeline &pipeline, CommandBuffer &command_buffer) const noexcept {
+ auto a = pipeline.build_texture(command_buffer, _a);
+ auto b = pipeline.build_texture(command_buffer, _b);
+ return luisa::make_unique(pipeline, this, a, b);
+}
+
+}// namespace luisa::render
+
+LUISA_RENDER_MAKE_SCENE_NODE_PLUGIN(luisa::render::MultiplyTexture)
diff --git a/src/textures/scale.cpp b/src/textures/scale.cpp
new file mode 100644
index 00000000..e45b34e3
--- /dev/null
+++ b/src/textures/scale.cpp
@@ -0,0 +1,79 @@
+//
+// Created by mike on 4/17/24.
+//
+
+#include
+#include
+#include
+
+namespace luisa::render {
+
+class ScaleTexture final : public Texture {
+
+private:
+ Texture *_base;
+ float4 _scale;
+ float4 _offset;
+
+public:
+ ScaleTexture(Scene *scene, const SceneNodeDesc *desc) noexcept
+ : Texture{scene, desc},
+ _base{scene->load_texture(desc->property_node("base"))},
+ _scale{[desc] {
+ auto s = desc->property_float_list_or_default("scale");
+ if (s.size() == 1u) { return make_float4(s[0]); }
+ s.reserve(4u);
+ if (s.size() < 4u) { s.resize(4u, 1.f); }
+ s.resize(4u);
+ return make_float4(s[0], s[1], s[2], s[3]);
+ }()},
+ _offset{[desc] {
+ auto o = desc->property_float_list_or_default("offset");
+ if (o.size() == 1u) { return make_float4(o[0]); }
+ o.reserve(4u);
+ if (o.size() < 4u) { o.resize(4u, 0.f); }
+ o.resize(4u);
+ return make_float4(o[0], o[1], o[2], o[3]);
+ }()} {}
+ [[nodiscard]] auto base() const noexcept { return _base; }
+ [[nodiscard]] auto scale() const noexcept { return _scale; }
+ [[nodiscard]] auto offset() const noexcept { return _offset; }
+ [[nodiscard]] bool is_black() const noexcept override { return false; }
+ [[nodiscard]] bool is_constant() const noexcept override { return _base->is_constant(); }
+ [[nodiscard]] luisa::optional evaluate_static() const noexcept override {
+ if (auto v = _base->evaluate_static()) {
+ return v.value() * _scale;
+ }
+ return nullopt;
+ }
+ [[nodiscard]] luisa::string_view impl_type() const noexcept override { return LUISA_RENDER_PLUGIN_NAME; }
+ [[nodiscard]] uint channels() const noexcept override { return _base->channels(); }
+ [[nodiscard]] luisa::unique_ptr build(
+ Pipeline &pipeline, CommandBuffer &command_buffer) const noexcept override;
+};
+
+class ScaleTextureInstance final : public Texture::Instance {
+
+private:
+ const Texture::Instance *_base;
+
+public:
+ ScaleTextureInstance(const Pipeline &pipeline, const Texture *node,
+ const Texture::Instance *base) noexcept
+ : Texture::Instance{pipeline, node}, _base{base} {}
+ [[nodiscard]] Float4 evaluate(const Interaction &it,
+ const SampledWavelengths &swl,
+ Expr time) const noexcept override {
+ return _base->evaluate(it, swl, time) * node()->scale() + node()->offset();
+ }
+};
+
+luisa::unique_ptr ScaleTexture::build(
+ Pipeline &pipeline, CommandBuffer &command_buffer) const noexcept {
+ auto base = pipeline.build_texture(command_buffer, _base);
+ return luisa::make_unique(pipeline, this, base);
+}
+
+}// namespace luisa::render
+
+LUISA_RENDER_MAKE_SCENE_NODE_PLUGIN(luisa::render::ScaleTexture)