【开发日志】2023.04 ZENO----Composite----CompNormalMap

news2024/9/24 2:33:01

NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/

CompNormalMap

将灰度图像转换为法线贴图

将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图:

  1. 加载灰度图像,并将其转换为浮点数值范围[0, 1]。

  2. 对于每个像素,计算其相邻像素与其距离,并计算它们之间的斜率。这可以通过使用Sobel算子或其他边缘检测算法来完成。

  3. 将每个像素的斜率向量归一化为单位长度,并转换为范围[-1, 1]内的值。

  4. 将每个像素的斜率向量转换为法线向量。法线向量可以通过使用以下公式计算得出:$normal = (2R - 1, 2G - 1, B)$,其中R,G和B分别是像素的红、绿和蓝通道值。由于我们的灰度图像没有颜色信息,我们可以将R,G和B设置为0.5,以得到一个朝向Z轴正方向的法线。

  5. 存储每个像素的法线向量作为输出法线贴图。

需要注意的是,这个简单的方法只能生成表面细节的近似值,并且可能会在处理像素边缘和细节时出现不准确的结果。更复杂的技术可以使用高斯过滤和其他方法来平滑法线图并产生更精细的结果。

Input:

Expect:

Output:

 

struct CompNormalMap : INode {
    virtual void apply() override {
        auto image = get_input<PrimitiveObject>("image");
        auto &ud = image->userData();
        int w = ud.get2<int>("w");
        int h = ud.get2<int>("h");
        using normal =  std::tuple<float, float, float>;
        normal n = {0, 0, 1};
        float n0 = std::get<0>(n);
        float n1 = std::get<1>(n);
        float n2 = std::get<2>(n);
        std::vector<normal> normalmap;
        normalmap.resize(image->size());
        float gx = 0;
        float gy = 0;
        float gz = 1;
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                int idx = i * w + j;
                if (i == 0 || i == h || j == 0 || j == w) {
                    normalmap[idx] = {0, 0, 1};
                }
            }
        }
        for (int i = 1; i < h-1; i++) {
            for (int j = 1; j < w-1; j++) {
                int idx = i * w + j;
                gx = (image->verts[idx+1][0] - image->verts[idx-1][0]) / 2.0f * 255;
                gy = (image->verts[idx+w][0] - image->verts[idx-w][0]) / 2.0f * 255;
                // 归一化法线向量
                float len = sqrt(gx * gx + gy * gy + gz * gz);
                gx /= len;
                gy /= len;
                gz /= len;
                // 计算光照值
                gx = 0.5f * (gx + 1.0f) ;
                gy = 0.5f * (gy + 1.0f) ;
                gz = 0.5f * (gz + 1.0f) ;
                normalmap[i * w + j] = {gx,gy,gz};
            }
        }
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                int idx = i * w + j;
                image->verts[i * w + j][0] = std::get<0>(normalmap[i * w + j]);
                image->verts[i * w + j][1] = std::get<1>(normalmap[i * w + j]);
                image->verts[i * w + j][2] = std::get<2>(normalmap[i * w + j]);
            }
        }
        set_output("image", image);
    }
};
ZENDEFNODE(CompNormalMap, {
    {
        {"image"}
    },
    {
        {"image"}
        },
    {},
    { "comp" },
});

cv

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat grayImage = imread("gray_image.png", IMREAD_GRAYSCALE);
    if (grayImage.empty())
    {
        cerr << "Could not read input image" << endl;
        return -1;
    }

    Mat normalMap(grayImage.size(), CV_8UC3);

    for (int i = 1; i < grayImage.rows - 1; i++)
    {
        for (int j = 1; j < grayImage.cols - 1; j++)
        {
            double dx = grayImage.at<uchar>(i, j + 1) - grayImage.at<uchar>(i, j - 1);
            double dy = grayImage.at<uchar>(i + 1, j) - grayImage.at<uchar>(i - 1, j);

            Vec3b normal(dx, dy, 255);
            normalize(normal, normal);

            normalMap.at<Vec3b>(i, j) = normal * 127.5 + Vec3b(127.5, 127.5, 127.5);
        }
    }

    imwrite("normal_map.png", normalMap);

    return 0;
}

不调库

#include <iostream>
#include <fstream>
#include <cmath>

using namespace std;

int main() {
    // 读取灰度图
    ifstream input("input.bmp", ios::binary);
    if (!input) {
        cout << "无法打开文件" << endl;
        return 1;
    }
    char header[54];
    input.read(header, 54);
    int width = *(int*)(header + 18);
    int height = *(int*)(header + 22);
    int row_size = (width * 24 + 31) / 32 * 4;
    char* data = new char[row_size * height];
    input.read(data, row_size * height);
    input.close();

    // 计算法线图
    char* output = new char[row_size * height];
    for (int y = 1; y < height - 1; y++) {
        for (int x = 1; x < width - 1; x++) {
            // 计算梯度
            double dx = (data[(y + 1) * row_size + (x + 1) * 3] - data[(y - 1) * row_size + (x - 1) * 3]) / 255.0;
            double dy = (data[(y - 1) * row_size + (x + 1) * 3] - data[(y + 1) * row_size + (x - 1) * 3]) / 255.0;
            double dz = 1.0;

            // 归一化法线向量
            double length = sqrt(dx * dx + dy * dy + dz * dz);
            dx /= length;
            dy /= length;
            dz /= length;

            // 计算光照值
            double light = dx * 0.5 + dy * -0.5 + dz * 0.5 + 0.5;
            int value = int(light * 255);
            output[y * row_size + x * 3] = value;
            output[y * row_size + x * 3 + 1] = value;
            output[y * row_size + x * 3 + 2] = value;
        }
    }

    // 输出法线图
    ofstream of("output.bmp", ios::binary);
    of.write(header, 54);
    of.write(output, row_size * height);
    of.close();

    delete[] data;
    delete[] output;
    return 0;
}
#include <iostream>
#include <vector>

// 计算Sobel算子
void sobel(const std::vector<float>& grayImage, int width, int height, std::vector<float>& dx, std::vector<float>& dy)
{
    dx.resize(width * height);
    dy.resize(width * height);

    for (int y = 1; y < height - 1; y++) {
        for (int x = 1; x < width - 1; x++) {
            float gx = -grayImage[(y - 1) * width + x - 1] + grayImage[(y - 1) * width + x + 1]
                     - 2.0f * grayImage[y * width + x - 1] + 2.0f * grayImage[y * width + x + 1]
                     - grayImage[(y + 1) * width + x - 1] + grayImage[(y + 1) * width + x + 1];
            float gy = grayImage[(y - 1) * width + x - 1] + 2.0f * grayImage[(y - 1) * width + x] + grayImage[(y - 1) * width + x + 1]
                     - grayImage[(y + 1) * width + x - 1] - 2.0f * grayImage[(y + 1) * width + x] - grayImage[(y + 1) * width + x + 1];

            dx[y * width + x] = gx;
            dy[y * width + x] = gy;
        }
    }
}

// 计算法向量
void normalMap(const std::vector<float>& grayImage, int width, int height, std::vector<float>& normal)
{
    std::vector<float> dx, dy;
    sobel(grayImage, width, height, dx, dy);

    normal.resize(width * height * 3);

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int i = y * width + x;
            float gx = dx[i];
            float gy = dy[i];

            float normalX = -gx;
            float normalY = -gy;
            float normalZ = 1.0f;

            float length = sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ);
            normalX /= length;
            normalY /= length;
            normalZ /= length;

            normal[i * 3 + 0] = normalX;
            normal[i * 3 + 1] = normalY;
            normal[i * 3 + 2] = normalZ;
        }
    }
}

int main()
{
    // 读取灰度图像
    std::vector<float> grayImage;
    int width, height;
    // TODO: 从文件中读取灰度图像到grayImage中,同时将宽度和高度存储在width和height中

   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452571.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文件 与 IO操作

前言 本篇介绍文件的基本操作&#xff0c;认识文本文件与二进制文件的区别&#xff0c;什么是绝对路径与相对路径&#xff0c;java标志库中又是如何进行文件操作的&#xff1b;认识流对象进行简单的文件读取操作&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让…

全网最详细,Pytest自动化测试框架关联/参数化实战,及拿即用...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Pytest自动化测试框…

从C出发 27 --- 深入理解指针与地址

指针是什么? 指针是变量&#xff0c;这种变量有一点特殊&#xff0c;它特殊在保存的值是内存地址&#xff0c;并且我们还可以通过指针所保存的内存地址来直接访问相应内存中的数据。 每一种指针类型的变量只保存对应类型变量的地址 比如这个类型的指针变量&#xff0c;只保…

计及源荷不确定性的综合能源生产单元运行调度与容量配置随机优化模型(matlab代码)

目录 1 主要内容 1.1 风光场景聚类 1.2 主模型程序结果 1.3 随机模型和确定性模型对比 1.4 有无储气对比 1.5 煤价灵敏性分析 1.6 甲烷价格灵敏性分析 2 部分程序 3 程序链接 1 主要内容 本程序复现《计及源荷不确定性的综合能源生产单元运行调度与容量配置两阶段随机…

Cloud Kernel SIG月度动态:发布 Anolis 8.8 镜像、kABI 社区共建流程

Cloud Kernel SIG&#xff08;Special Interest Group&#xff09;&#xff1a;支撑龙蜥内核版本的研发、发布和服务&#xff0c;提供生产可用的高性价比内核产品。 01 SIG 整体进展 Anolis 8.8 镜像发布&#xff0c;默认搭载 ANCK 5.10-013 版本。 Anolis 23 滚动内核更新至…

4月更新!EasyOps®全平台27项新功能一口气来袭~

又到了每月产品盘点时刻&#xff0c;27大新功能上线和升级优化&#xff0c;设计Hyperlnsight超融合持续观测平台、DevOps持续交付平台、AutoOps自动化运维平台、ITSM服务平台、公共服务&#xff0c;在不断的技术创新过程中&#xff0c;进一步加速IT运维效率升级。 下面和小编一…

Vue监视数据的学习笔记

Vue监测数据变化的更新 <div id"monitor"><h2>人员列表</h2><button click"updateMei">更新马冬梅信息</button><ul><li v-for"(p,index) of persons" :key"index">{{p.name}}--{{p.age}}…

Python数据结构-----递归实现快速排序

目录 前言&#xff1a; 快速排序 1.概念 2.示例 Python代码实现 递归实现快速排序 前言&#xff1a; 今天我们就来一起学习快速排序的思想方法&#xff0c;然后通过Python语言来实现快速排序的功能&#xff0c;下面我们就开始今天的学习吧&#xff01; 快速排序 1.概念 快…

银行数字化转型导师坚鹏:金融科技在银行数字化转型中的创新案例

金融科技在银行数字化转型中的创新与应用案例&#xff08;偏重对公业务&#xff09; 课程背景&#xff1a; 数字化背景下&#xff0c;很多银行存在以下问题&#xff1a; 不清楚5G如何赋能银行数字化转型&#xff1f; 不清楚最新金融科技创新与应用案例&#xff1f; 不了…

【部署YUM仓库】

目录 一、YUM概述1.1、YUM&#xff08; Yellow dog Upadater Modified) 二、准备安装源2.1、YUM软件仓库类型2.2、RPM软件包的来源2.3、第三方yum源的网址查看:2.4、构建CentOS 7 软件仓库2.4.1、配置yum源仓库时要先挂载2.4.2、配置yum源仓库2.4.3、配置完后更新缓存 2.5、ftp…

AutoSAR内存映射

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录 为了防止不必要的内存缺口&#xff08;RAM 中未使用的空间&#xff09;&#xff0c;不同大小&#xff08;8、16 和 32 位&#xff09;的变量根据其大小映射到特…

论文笔记:基于并行注意力 UNet的裂缝检测方法

0 简介 论文&#xff1a;基于并行注意力 UNet的裂缝检测方法&#xff08;Parallel Attention Based UNet for Crack Detection&#xff09;&#xff1b; 发表&#xff1a;2021年发表在《计算机研究与发展》上。 1 问题分析 问题&#xff1a;裂缝图像中存在噪声、光线、阴影等…

中睿天下亮相2022电力行业信息化年会

4月14日-15日&#xff0c;以“低碳数字新动力&#xff0c;电力转型新发展”为主题的2022电力行业信息化年会在长沙成功召开。中睿天下作为网络安全企业受邀出席参展&#xff0c;展示多样性网络安全产品、电力行业解决方案及相关应用成果。 作为能源电力领域的行业盛会和学术交流…

垃圾回收器(一)

垃圾回收器概述 垃圾收集器没有在规范中进行过多的规定&#xff0c;可以由不同的厂商、不同版本的JVM来实现。 由于JDK的版本处于高速迭代过程中&#xff0c;因此Java发展至今已经衍生了众多的GC版本。 从不同角度分析垃圾收集器&#xff0c;可以将GC分为不同的类型。 GC分…

设计模式简谈

设计模式是我们软件架构开发中不可缺失的一部分&#xff0c;通过学习设计模式&#xff0c;我们可以更好理解的代码的结构和层次。 设计原则 设计原则是早于设计方法出现的&#xff0c;所以的设计原则都要依赖于设计方法。这里主要有八个设计原则。 推荐一个零声学院免费教程&…

阳光万里,祝你上岸——免统考在职研究生

什么是在职研究生 在职研究生&#xff0c;是国家计划内&#xff0c;以在职人员身份&#xff0c;部分时间在职工作&#xff0c;部分时间在校学习的研究生教育的一种类型。在职攻读硕士方式有三种&#xff1a; 1.双证非全日制研究生&#xff1a;为普通高等教育研究生学历&#x…

QGIS--开发OpenSCENARIO动态场景(三)--制作动态场景

一、添加scenario&#xff0c;carla的环境变量 export CARLA_ROOT/path/to/your/carla/installation export SCENARIO_RUNNER_ROOT/path/to/your/scenario/runner/installation export PYTHONPATH$PYTHONPATH:${CARLA_ROOT}/PythonAPI/carla/dist/carla-<VERSION>.egg ex…

如何利用工时表来帮助项目管理做得更完善?

项目管理是一项复杂的任务&#xff0c;需要协调各种资源以确保项目按时交付。其中一个关键方面是管理各个员工工时。工时表软件是一种可以帮助企业记录各个员工工作时效的工具&#xff0c;而且还可以帮助项目管理者记录和跟踪项目成员的时间。那么如何利用工时表来帮助项目管理…

如何配置静态路由?这个实例详解交换机的静态路由配置

一、什么是静态路由 静态路由是一种路由的方式&#xff0c;它需要通过手动配置。静态路由与动态路由不同&#xff0c;静态路由是固定的&#xff0c;不会改变。一般来说&#xff0c;静态路由是由网络管理员逐项加入路由表&#xff0c;简单来说&#xff0c;就是需要手动添加的。…

相空间相关概念以及轨迹生成

在时间序列分析中&#xff0c;相位轨迹也被广泛应用于提取隐藏在数据中的结构信息。例如&#xff0c;在人类活动识别任务中&#xff0c;通过构建不同活动对应的相位轨迹&#xff0c;可以揭示活动间的相似性和差异性&#xff0c;从而有助于设计有效的分类方法。 相空间&#xff…