【开发日志】2023.04 ZENO----Composite----CompNormalMap
NormalMap-Online (cpetry.github.io)
https://cpetry.github.io/NormalMap-Online/
CompNormalMap
将灰度图像转换为法线贴图
将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图:
-
加载灰度图像,并将其转换为浮点数值范围[0, 1]。
-
对于每个像素,计算其相邻像素与其距离,并计算它们之间的斜率。这可以通过使用Sobel算子或其他边缘检测算法来完成。
-
将每个像素的斜率向量归一化为单位长度,并转换为范围[-1, 1]内的值。
-
将每个像素的斜率向量转换为法线向量。法线向量可以通过使用以下公式计算得出:$normal = (2R - 1, 2G - 1, B)$,其中R,G和B分别是像素的红、绿和蓝通道值。由于我们的灰度图像没有颜色信息,我们可以将R,G和B设置为0.5,以得到一个朝向Z轴正方向的法线。
-
存储每个像素的法线向量作为输出法线贴图。
需要注意的是,这个简单的方法只能生成表面细节的近似值,并且可能会在处理像素边缘和细节时出现不准确的结果。更复杂的技术可以使用高斯过滤和其他方法来平滑法线图并产生更精细的结果。
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中
相关文章:
【开发日志】2023.04 ZENO----Composite----CompNormalMap
NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/ CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…...
春秋云境:CVE-2022-28525 (文件上传漏洞)
目录 一、题目 1.登录 2.burp抓包改包 3.蚁剑获取flag 一、题目 ED01CMSv20180505存在任意文件上传漏洞 英语不够 翻译来凑: 点击其他页面会Not Found 找不到: 先登录看看吧: 试试万能密码:admin:123 发现错误…...
【软件测试二】开发模型和测试模型,BUG概念篇
目录 1.软件的生命周期 2.瀑布模型 3.螺旋模型 4.增量,迭代 5.敏捷---scrum 1. 敏捷宣言 2.角色 6. 软件测试v模型 7.软件测试w模型 8.软件测试的生命周期 9.如何描述一个BUG 10.如何定义BUG的级别 11.BUG的生命周期 12.产生争执怎么办 1.软件的生命周期…...
短视频app开发:如何实现视频直播功能
短视频源码的实现 在短视频app开发中,实现视频直播功能需要借助短视频源码。短视频源码可以提供一个完整的视频直播功能模块,包括视频采集、编码、推流等。因此,我们可以选择一些开源的短视频源码,例如LFLiveKit、ijkplayer等&am…...
[架构之路-174]-《软考-系统分析师》-5-数据库系统-7-数据仓库技术与数据挖掘技术
5 . 7 数据仓库技术 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。近年来,人们对数据仓库技术的关注程度越来越尚,其原因是过去的几十年中,建设了无数的应用系统,积累了…...
销售高品质 FKM EPDM NBR 硅胶 O 形密封圈
O形圈常用于各种行业,包括汽车、航空航天和制造业。它们是由不同材料制成的圆环,用于将两个或多个组件密封在一起。用于制造O形圈的材料是决定其有效性和耐用性的重要因素。在本文中,我们将讨论用于制作O形圈的不同类型的材料。 1.丁腈橡胶(…...
Linux环境变量:不可或缺的系统组成部分
目录标题 引言(Introduction)Linux环境变量的概念(Concept of Linux Environment Variables)环境变量的作用与重要性(Roles and Importance of Environment Variables) Linux环境变量基础(Linux…...
FFmpeg命令行解析
目录标题 一、引言(Introduction)1.1 FFmpeg简介(Overview of FFmpeg)1.2 FFmpeg命令行的应用场景(Application Scenarios of FFmpeg Command Line) 二、FFmpeg命令行基础(FFmpeg Command Line …...
机器学习——为什么逻辑斯特回归(logistic regression)是线性模型
问:逻辑斯蒂回归是一种典型的线性回归模型。 答:正确。逻辑斯蒂回归是一种典型的线性回归模型。它通过将线性回归模型的输出结果映射到[0,1]区间内,表示某个事物发生的概率,从而适用于二分类问题。具体地说,它使用sig…...
从输入URL到页面展示到底发生了什么
刚开始写这篇文章还是挺纠结的,因为网上搜索“从输入url到页面展示到底发生了什么”,你可以搜到一大堆的资料。而且面试这道题基本是必考题,二月份面试的时候,虽然知道这个过程发生了什么,不过当面试官一步步追问下去的…...
Qt connect传参方式及lambda函数传参方式详解
Qt connect传参方式及lambda函数传参方式详解 Qt是一种流行的跨平台C应用程序框架,它提供了许多有用的工具和函数来帮助开发人员构建高效的图形用户界面和其他应用程序。其中,Qt Connect函数是用于连接信号和槽的重要函数之一,它可以在Qt应用…...
如何在硬盘上恢复已经删除的照片?
可以从硬盘恢复删除的照片吗? 旅行后,许多人倾向于将照片保存到另一个储存设备作为副本或备份。例如,将它们存储在外部硬盘上或将图片传输到电脑。但是在整理照片的时候,很可能不小心把照片删掉了,尤其是使用外接硬…...
Unity日记22(携程概念)
目录 学习视频 携程 1异步 2调用方法 3优点 4停止方法 5返回值 实例:每过一秒打印当前运行时间 实例:停止数字打印携程 错误方法:(携程只能开一个) 参考方法 学习视频 https://www.bilibili.com/video/BV1eu…...
01-Linux-磁盘分区与目录配置
1. 主引导纪录和磁盘分区表 1.1 MBR分区表 启动引导程序记录区与分区表都放在磁盘的第一个扇区(512B) 由于分区表仅占 64B,因此最多能有四组记录区,每组记录区记录了该区段的起始与结束的柱面号码。 缺点如下: 操作…...
连接器信号完整性仿真教程 二
在连接器信号完整性仿真教程一中Step by Step演示了如何进行连接器信号完整性仿真,看完这片博文后应该可以做类似产品的仿真。如果说,看了这篇博文就学会了连接器信号完整性仿真,那就有点过了。有人也许会说信号完整性仿真难学,不…...
基于深度学习的图片上色(Opencv,Pytorch,CNN)
文章目录 1. 前言2.图像格式(RGB,HSV,Lab)2.1 RGB2.2 hsv2.3 Lab 3. 生成对抗网络(GAN)3.1 生成网络(Unet)3.2 判别网络(resnet18) 4. 数据集5. 模型训练与预…...
Python爬虫
目录 爬虫总览 准备工作 一、爬虫基础 1、爬虫前导 1.1、爬虫介绍 1.2、HTTP与HTTPS 1.3、URL 1.4、开发工具 1.5、爬虫流程 2、requests模块 2.1、简介 2.2、安装 2.3、发送请求 二、爬虫 爬虫总览 准备工作 一、爬虫基础 1、爬虫前导 1.1、爬虫介绍 概念&…...
python基础案例题:进制转换、字符串加密的实现、猜拳游戏、多种方法计算π
目录 前言1.进制转换2.字符串加密的实现3.猜拳游戏4.多种方法计算π尾语 💝 前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 1.进制转换 功能: 获取十进制整数的二进制串,相当于内置函数bin。 算法分析: 对2辗转相除&…...
Spring Boot入门与进阶
本文将为您详细讲解Spring Boot的入门与进阶知识,包括Spring Boot的简介、环境搭建、基本功能以及高级特性,并配以丰富的代码示例,帮助大家快速掌握Spring Boot。 一、Spring Boot简介 Spring Boot是基于Spring框架的一种轻量级、快速开发的…...
servlet(1)—javaEE
文章目录 1.认识servlet2.使用servlet2.1创建项目2.2引入依赖2.3创建目录2.4编写代码2.5打包2.6部署2.7运行2.8验证 3.开发步骤4.部署方式4.1打包4.2安装插件 5.访问出错的情况5.1 4045.2 4055.3其他 6.servlet的三大生命周期方法7.servlet api7.1HttpServlet7.2HttpServletReq…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

