Games101学习 - 着色

本文主要讲述Games101中的着色部分。

文中将使用UE的UTexture2D接口,若不了解可以看这篇:
https://blog.csdn.net/grayrail/article/details/142165442

1.面积比计算三角形坐标

在这里插入图片描述
通过三角形面积比可以得到三角形的坐标alpha、beta、gamma从而进行插值,或是进行图像纹理绘制,基于上一篇学习文章的三角形绘制脚本:
https://blog.csdn.net/grayrail/article/details/142211284

增加面积比插值后脚本如下:

#include "MyBlueprintFunctionLibrary.h"float TriangleArea(const FVector2D& A, const FVector2D& B, const FVector2D& C)
{// 计算向量AB和AC的叉乘float CrossProduct = (B.X - A.X) * (C.Y - A.Y) - (B.Y - A.Y) * (C.X - A.X);// 返回三角形面积(取叉乘结果的绝对值的一半)return FMath::Abs(CrossProduct) * 0.5f;
}// 计算P点的重心坐标
FVector GetBarycentricCoordinates(const FVector2D& P, const FVector2D& A, const FVector2D& B, const FVector2D& C)
{// 计算ABC的总面积float AreaABC = TriangleArea(A, B, C);// 计算PBC, PCA, PAB的面积float AreaPBC = TriangleArea(P, B, C);float AreaPCA = TriangleArea(P, C, A);float AreaPAB = TriangleArea(P, A, B);// 重心坐标float alpha = AreaPBC / AreaABC;float beta = AreaPCA / AreaABC;float gamma = AreaPAB / AreaABC;return FVector(alpha, beta, gamma); // 返回重心坐标(alpha, beta, gamma)
}UTexture2D* UMyBlueprintFunctionLibrary::GenTexture(int32 Width, int32 Height)
{// 创建临时纹理UTexture2D* NewTexture = UTexture2D::CreateTransient(Width, Height);// 配置纹理NewTexture->MipGenSettings = TMGS_NoMipmaps;NewTexture->CompressionSettings = TC_VectorDisplacementmap;NewTexture->SRGB = false;// 锁定纹理数据进行写入FTexture2DMipMap& Mip = NewTexture->PlatformData->Mips[0];void* TextureData = Mip.BulkData.Lock(LOCK_READ_WRITE);// 设置默认颜色为黑色FColor* FormattedImageData = static_cast<FColor*>(TextureData);for (int32 y = 0; y < Height; ++y){for (int32 x = 0; x < Width; ++x){FormattedImageData[y * Width + x] = FColor::Black; // 背景颜色设置为黑色}}// 定义三角形顶点(A, B, C)FVector2D A(Width / 2, Height / 4);  // 三角形顶点AFVector2D B(Width / 4, 3 * Height / 4); // 三角形顶点BFVector2D C(3 * Width / 4, 3 * Height / 4); // 三角形顶点C// 深红色FColor TriangleColor = FColor(139, 0, 0, 255);// 叉乘判断点P是否在三角形ABC内auto IsPointInTriangle = [](const FVector2D& P, const FVector2D& A, const FVector2D& B, const FVector2D& C) -> bool{FVector2D AP = P - A;FVector2D BP = P - B;FVector2D CP = P - C;FVector2D AB = B - A;FVector2D BC = C - B;FVector2D CA = A - C;// 叉乘结果float Cross1 = AB.X * AP.Y - AB.Y * AP.X; // AB 和 AP 的叉乘float Cross2 = BC.X * BP.Y - BC.Y * BP.X; // BC 和 BP 的叉乘float Cross3 = CA.X * CP.Y - CA.Y * CP.X; // CA 和 CP 的叉乘// 如果三个叉乘结果符号相同,则点在三角形内return (Cross1 >= 0 && Cross2 >= 0 && Cross3 >= 0) || (Cross1 <= 0 && Cross2 <= 0 && Cross3 <= 0);};int SubPixelCount = 8;// 超采样抗锯齿:子像素划分float SubPixelStep = 1.0f / SubPixelCount; // 子像素的步长int32 TotalSubPixels = SubPixelCount * SubPixelCount; // 子像素的总数// 遍历每个像素并应用抗锯齿逻辑for (int32 y = 0; y < Height; ++y){for (int32 x = 0; x < Width; ++x){int32 CoveredSubPixels = 0;// 遍历 SubPixelCount x SubPixelCount 子像素for (int32 subY = 0; subY < SubPixelCount; ++subY){for (int32 subX = 0; subX < SubPixelCount; ++subX){FVector2D SubPixelPos = FVector2D(x + (subX + 0.5f) * SubPixelStep, y + (subY + 0.5f) * SubPixelStep); // 子像素位置if (IsPointInTriangle(SubPixelPos, A, B, C)){CoveredSubPixels++;}}}// 计算覆盖率并设置像素颜色float Coverage = static_cast<float>(CoveredSubPixels) / TotalSubPixels; // 覆盖率(0 到 1)if (Coverage > 0){FVector2D P(x, y);FVector BaryCoords = GetBarycentricCoordinates(P, A, B, C);BaryCoords.X = FMath::RoundToInt(BaryCoords.X * 255 * Coverage);BaryCoords.Y = FMath::RoundToInt(BaryCoords.Y * 255 * Coverage);BaryCoords.Z = FMath::RoundToInt(BaryCoords.Z * 255 * Coverage);FColor FinalColor = FColor(BaryCoords.X, BaryCoords.Y, BaryCoords.Z);FormattedImageData[y * Width + x] = FinalColor;}}}// 解锁纹理数据Mip.BulkData.Unlock();NewTexture->UpdateResource();return NewTexture;
}

绘制结果如下:
在这里插入图片描述

此外,gamma插值信息的获取,也可以这样修改:

// 重心坐标
float alpha = AreaPBC / AreaABC;
float beta = AreaPCA / AreaABC;
float gamma = 1 - alpha - beta;

2.双线性插值 Bilinear

因为实际屏幕采样像素时需要考虑到材质大小、屏幕占比等因素,采样图片并不会像CPU采样那样都是整数,而这种0-1浮点数的坐标采样会带来走样问题,因此通过Bilinear双线性采样的方式采样周围4个像素并进行插值,从而得到更好的采样结果:
在这里插入图片描述
代码如下:

UTexture2D* UMyBlueprintFunctionLibrary::GenTexture(int32 Width, int32 Height, UTexture2D* SourceTexture)
{if (!SourceTexture){UE_LOG(LogTemp, Error, TEXT("SourceTexture is null"));return nullptr;}// 创建一个新的UTexture2DUTexture2D* NewTexture = UTexture2D::CreateTransient(Width, Height, SourceTexture->GetPixelFormat());if (!NewTexture){UE_LOG(LogTemp, Error, TEXT("Failed to create new texture"));return nullptr;}// 锁定源纹理和新纹理的内存FTexture2DMipMap& SourceMip = SourceTexture->PlatformData->Mips[0];FTexture2DMipMap& DestMip = NewTexture->PlatformData->Mips[0];// 获取源纹理的像素数据uint8* SourcePixels = static_cast<uint8*>(SourceMip.BulkData.Lock(LOCK_READ_ONLY));uint8* DestPixels = static_cast<uint8*>(DestMip.BulkData.Lock(LOCK_READ_WRITE));int32 SourceWidth = SourceMip.SizeX;int32 SourceHeight = SourceMip.SizeY;int32 PixelSize = 4; // 假设使用的是标准的8位RGBA纹理// 进行采样并将数据写入到新纹理中for (int32 y = 0; y < Height; ++y){for (int32 x = 0; x < Width; ++x){// 计算源纹理中的浮动坐标float U = static_cast<float>(x) / Width * (SourceWidth - 1);float V = static_cast<float>(y) / Height * (SourceHeight - 1);// 获取四个邻近像素的坐标int32 X0 = static_cast<int32>(FMath::FloorToInt(U));int32 X1 = FMath::Clamp(X0 + 1, 0, SourceWidth - 1);int32 Y0 = static_cast<int32>(FMath::FloorToInt(V));int32 Y1 = FMath::Clamp(Y0 + 1, 0, SourceHeight - 1);// 获取插值因子float FracX = U - X0;float FracY = V - Y0;// 计算四个顶点的索引int32 Index00 = (Y0 * SourceWidth + X0) * PixelSize;int32 Index01 = (Y1 * SourceWidth + X0) * PixelSize;int32 Index10 = (Y0 * SourceWidth + X1) * PixelSize;int32 Index11 = (Y1 * SourceWidth + X1) * PixelSize;// 线性插值四个像素点auto Lerp = [](uint8 A, uint8 B, float T) -> uint8 {return FMath::Clamp(static_cast<int32>(FMath::Lerp(static_cast<float>(A), static_cast<float>(B), T)), 0, 255);};// 对R、G、B、A分别进行插值uint8 R = Lerp(Lerp(SourcePixels[Index00], SourcePixels[Index10], FracX),Lerp(SourcePixels[Index01], SourcePixels[Index11], FracX),FracY);uint8 G = Lerp(Lerp(SourcePixels[Index00 + 1], SourcePixels[Index10 + 1], FracX),Lerp(SourcePixels[Index01 + 1], SourcePixels[Index11 + 1], FracX),FracY);uint8 B = Lerp(Lerp(SourcePixels[Index00 + 2], SourcePixels[Index10 + 2], FracX),Lerp(SourcePixels[Index01 + 2], SourcePixels[Index11 + 2], FracX),FracY);uint8 A = Lerp(Lerp(SourcePixels[Index00 + 3], SourcePixels[Index10 + 3], FracX),Lerp(SourcePixels[Index01 + 3], SourcePixels[Index11 + 3], FracX),FracY);// 写入目标纹理int32 DestIndex = (y * Width + x) * PixelSize;DestPixels[DestIndex] = R;DestPixels[DestIndex + 1] = G;DestPixels[DestIndex + 2] = B;DestPixels[DestIndex + 3] = A;}}// 解锁像素数据SourceMip.BulkData.Unlock();DestMip.BulkData.Unlock();// 更新新纹理NewTexture->UpdateResource();return NewTexture;
}

注意需要更新蓝图节点:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI技术好书推荐:《AI系统-原理与架构》

今年1月份在B站发现了一个B站宝藏博主&#xff0c;发布的一系列AI技术类科普视频内容很干&#xff0c;逻辑清晰&#xff0c;很多知识点讲的深入浅出&#xff0c;非常有用&#xff0c;被直接种粉。 后来这一系列的课程内容博主有了出书的计划&#xff0c;机缘巧合有幸参与部分章…

CSS入门笔记

目录 概述 组成 CSS 语法 常见的使用方式 CSS 优先级 CSS 选择器 1. 基本选择器 2. 属性选择器 3. 伪类选择器 4. 组合选择器 示例 优先级 边框样式与盒子模型 单个边框 边框轮廓&#xff08;Outline&#xff09; 盒子模型 模型介绍 边距设置 布局示例 文…

计算机考研408-计算机网络

【题33】下列选项中&#xff0c;不属于网络体系结构所描述的内容是&#xff08;&#xff09; A.网络的层次 B.每一层使用的协议 C.协议的内部实现细节 D.每一层必须完成的功能 解析&#xff1a; 本题考查的是网络体系结构相关的概念。 图1描述了网络的7层架构以及每一层所要完成…

Python模块和包:标准库模块(os, sys, datetime, math等)②

文章目录 一、os 模块1.1 获取当前工作目录1.2 列出目录内容1.3 创建和删除目录1.4 文件和目录操作 二、sys 模块2.1 获取命令行参数2.2 退出程序2.3 获取 Python 版本信息 三、datetime 模块3.1 获取当前日期和时间3.2 日期和时间的格式化3.3 日期和时间的运算 四、math 模块4…

代理IP批理检测工具,支持socks5,socks4,http和https代理批量检测是否可用

代理IP批理检测工具,支持socks5,socks4,http和https代理批量检测是否可用 工具使用c编写&#xff1a; 支持ipv4及ipv6代理服务器。 支持http https socks4及socks5代理的批量检测。 支持所有windows版本运行&#xff01; 导入方式支持手工选择文件及拖放文件。 导入格式支持三…

【我的 PWN 学习手札】劫持 tcache_perthread_struct

目录 前言 一、tcache perthread struct 二、劫持 tcache_perthread_struct 三、测试与模板 前言 tcache 是 glibc 2.26 (ubuntu 17.10) 之后引入的一种技术&#xff0c;目的是提升堆管理的性能&#xff0c;与 fast bin 类似。 tcache 引入了两个新的结构体&#xff0c; tc…

机器学习之非监督学习(四)K-means 聚类算法

机器学习之非监督学习&#xff08;一&#xff09;K-means 聚类算法 0. 文章传送1.非监督学习定义2.非监督学习分类2.1 聚类 Clustering2.2 异常检测 Anomaly Detection 3.K-means聚类算法 K-means clustering案例引入算法步骤算法优化成本函数初始化方法K的选择 代码实现 4.案例…

ElementUI 布局——行与列的灵活运用

ElementUI 布局——行与列的灵活运用 一 . 使用 Layout 组件1.1 注册路由1.2 使用 Layout 组件 二 . 行属性2.1 栅格的间隔2.2 自定义元素标签 三 . 列属性3.1 列的偏移3.2 列的移动 在现代网页设计中&#xff0c;布局是构建用户界面的基石。Element UI 框架通过其强大的 <e…

learn C++ NO.17——继承

什么是继承&#xff1f; 用冒号 : 后跟基类名称来声明一个类是从某个基类继承而来的。继承方式可以是 public、protected 或 private&#xff0c;这决定了基类成员在子类中的访问权限。 下面通过代码简单进行一下演示. 派生类Student即子类&#xff0c;而基类Person是它的父…

Ubuntu22.04安装paddle

查看系统版本信息 使用命令lsb_release -a查看系统版本 rootLAIS01:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.5 LTS Release: 22.04 Codename: jammy查看系统支持的cuda版本&#xff0c;使用命令nvidia-smi&#…

基于paddleocr的批量图片缩放识别

说明 在进行ocr文字识别的时候&#xff0c;有时候我们需要使用批量测试的功能&#xff0c;但是有些图片会识别失败或者个别根本识别不出来&#xff0c;这时候我们可以通过对原图片进行缩放&#xff0c;提高图像的分辨率&#xff0c;然后再次识别&#xff0c;这样可以大大提高图…

Canal+RabbitMQ数据同步环境配置

Canal 是阿里巴巴开发的开源工具&#xff0c;主要用于解析 MySQL 的 binlog 日志&#xff0c;从而实现数据同步。Canal 会模拟 MySQL 从库的协议&#xff0c;订阅主库的 binlog&#xff0c;从而获取数据库的变更信息。 将 Canal 解析到的 MySQL 数据库变更消息通过 RabbitMQ 分…

青柠视频云——视频丢包(卡顿、花屏、绿屏)排查

一、问题说明 近期有客户反馈&#xff0c;接入平台的设备经常出来卡顿、花屏、录屏的情况&#xff0c;出现这样的场景很是尴尬。 客户是私有化部署在公网环境&#xff0c;于是我们联系客户&#xff0c;对问题进行追踪排查。 二、场景复现 我们现场情况确认的过程中&#xff0c;…

蓝桥杯嵌入式客观题合集

十四届模拟赛二客观题 解析&#xff1a;STM32微控制器的I/O端口寄存器必须按32位字被访问 解析&#xff1a;微分电路能将三角波转换为方波&#xff1b;积分电路能将方波转换为三角波 解析&#xff1a;放大电路的本质是能量的控制与转换 解析&#xff1a;具有n个节点&#xff0c…

修改Docker默认存储路径,解决系统盘占用90%+问题(修改docker root dir)

随着Docker技术的广泛应用&#xff0c;它极大地简化了复杂项目的部署与维护流程&#xff0c;仅凭单一镜像即可轻松运行。然而&#xff0c;随着数据量不断增长&#xff0c;Docker的默认数据存储方式可能逐渐成为挑战&#xff0c;尤其是当默认安装于根目录&#xff08;“/”&…

【雪球-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

Python酷库之旅-第三方库Pandas(104)

目录 一、用法精讲 451、pandas.DataFrame.pow方法 451-1、语法 451-2、参数 451-3、功能 451-4、返回值 451-5、说明 451-6、用法 451-6-1、数据准备 451-6-2、代码示例 451-6-3、结果输出 452、pandas.DataFrame.dot方法 452-1、语法 452-2、参数 452-3、功能…

【C++】STL简介

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a;STL || C 目录 前言什么是STL&#xff1f;STL的历史STL的版本STL六大组件STL的优缺点STL的优点&#xff1a;STL的缺点&#xff1a; 如何学习STL结语 前言 本篇博客主要内容&#xff1a;ST…

灾备技术演进之路 | 虚拟化无代理备份只能挂载验证和容灾吗?只能无代理恢复吗?且看科力锐升级方案

灾备技术演进之路系列 虚拟化备份技术演进 摆脱束缚&#xff0c;加速前行 无代理备份仅能挂载/恢复验证吗&#xff1f; ——科力锐极简验证演练无代理备份来了 无代理备份无法应对平台级故障吗&#xff1f; ——科力锐应急接管无代理备份来了 无代理备份仅能同平台挂载吗&a…

Llama 3.1 Omni:颠覆性的文本与语音双输出模型

你可能听说过不少关于语言模型的进展,但如果告诉你,有一种模型不仅能生成文本,还能同时生成语音,你会不会觉得特别酷?今天咱们就来聊聊一个相当前沿的项目——Llama 3.1 Omni模型。这个模型打破了传统的文字生成边界,直接让文本和语音同时输出,实现了真正的"多模态…