当前位置: 首页 > news >正文

Shader开发(十六)UV 坐标介绍

在现代计算机图形学中,纹理映射技术的核心基础是纹理坐标系统,即UV坐标系统。本节将深入探讨UV坐标的理论原理、命名规范以及在着色器中的具体实现方法,为后续的纹理采样技术奠定理论基础。


UV 坐标的定义与应用

命名规范

纹理坐标系统采用UV命名规范而非传统的XY坐标命名,这一设计源于计算机图形学中对不同坐标系统的严格区分需求。在三维图形渲染中,XY轴通常用于表示空间位置坐标,为避免概念混淆和提高代码可读性,纹理坐标系统采用了独立的命名约定:

  • U轴:对应纹理的水平方向(X轴)

  • V轴:对应纹理的垂直方向(Y轴)

这种命名规范已成为图形学领域的标准惯例,确保了不同坐标系统之间的清晰区分。

坐标定义

UV坐标系统采用标准化的坐标范围[0,1],具有以下特征:

  • 原点位置:纹理左下角为坐标原点(0,0)

  • 坐标范围:U轴和V轴均在[0,1]区间内

  • 增长方向:从左下角向右上角方向递增

标注了UV坐标的纹理

坐标映射

以图所示的纹理为例,片元着色器通过UV坐标(0.8,0.3)可精确访问纹理右下区域的羽毛颜色信息。这种映射机制使得着色器能够根据几何表面的位置动态获取对应的纹理颜色数据。


向网格添加 UV 坐标

在 openFrameworks 中,向网格添加 UV 坐标类似于添加颜色属性,使用 addTexCoord() 函数。需确保坐标顺序与顶点位置一致,且 V 轴向上递增以匹配窗口顶部:

// 按照逆时针顺序为四个顶点分配UV坐标
quad.addTexCoord(glm::vec2(0, 0));  // 左下角
quad.addTexCoord(glm::vec2(0, 1));  // 左上角  
quad.addTexCoord(glm::vec2(1, 1));  // 右上角
quad.addTexCoord(glm::vec2(1, 0));  // 右下角

着色器实现UV坐标传递

顶点着色器

UV坐标在渲染管线中的传递机制遵循标准的顶点属性处理流程。顶点着色器负责接收CPU传递的UV坐标数据,并将其传递给片元着色器进行插值处理。

#version 410layout(location = 0) in vec3 pos;     // 顶点位置属性
layout(location = 3) in vec2 uv;      // UV坐标属性out vec2 fragUV;                      // 输出到片元着色器的UV坐标void main()
{gl_Position = vec4(pos, 1.0);     // 标准位置变换fragUV = uv;                      // UV坐标直接传递
}

顶点属性布局规范

OpenFrameworks框架对顶点属性采用了标准化的布局分配机制:

  1. 位置属性 (Position) - location = 0

  2. 颜色属性 (Color) - location = 1

  3. 法线属性 (Normal) - location = 2

  4. UV坐标属性 - location = 3

这种布局规范确保了不同类型顶点数据的有序组织,提高了着色器程序的可维护性和跨平台兼容性。

片元着色器的UV可视化

片元着色器接收插值后的 UV 坐标,并将其映射为颜色输出(U → 红色通道,V → 绿色通道)。预期结果:角部颜色分别为红色、绿色、黄色和黑色,中间区域渐变混合:

#version 410in vec2 fragUV;                       // 从顶点着色器接收的插值UV坐标
out vec4 outCol;                      // 最终输出颜色void main()
{// 将UV坐标映射为RGB颜色分量outCol = vec4(fragUV, 0.0f, 1.0f); 
}

渲染结果

颜色分布

执行上述着色器代码后,屏幕将呈现特定的颜色分布模式:

  • 左下角:黑色 (UV: 0,0 → RGB: 0,0,0)

  • 右下角:红色 (UV: 1,0 → RGB: 1,0,0)

  • 左上角:绿色 (UV: 0,1 → RGB: 0,1,0)

  • 右上角:黄色 (UV: 1,1 → RGB: 1,1,0)

这种颜色分布验证了UV坐标插值机制的正确性,为后续的纹理采样操作提供了可靠的坐标基础。

显示结果如下:


结论

UV坐标系统是纹理映射技术的核心基础设施。通过严格的命名规范、标准化的坐标范围以及高效的着色器传递机制,UV坐标系统为复杂的纹理采样操作提供了可靠的技术支撑。掌握UV坐标的理论原理和实现方法,是深入学习纹理映射技术的必要前提。


项目代码参考

ofApp.h
#pragma once#include "ofMain.h"class ofApp : public ofBaseApp{public:void setup();void update();void draw();void keyPressed(int key);void keyReleased(int key);void mouseMoved(int x, int y );void mouseDragged(int x, int y, int button);void mousePressed(int x, int y, int button);void mouseReleased(int x, int y, int button);void mouseEntered(int x, int y);void mouseExited(int x, int y);void windowResized(int w, int h);void dragEvent(ofDragInfo dragInfo);void gotMessage(ofMessage msg);ofMesh quad;ofShader shader;
};
ofApp.cpp
#include "ofApp.h"//--------------------------------------------------------------
void ofApp::setup()
{quad.addVertex(glm::vec3(-1, -1, 0));quad.addVertex(glm::vec3(-1, 1, 0));quad.addVertex(glm::vec3(1, 1, 0));quad.addVertex(glm::vec3(1, -1, 0));quad.addColor(ofDefaultColorType(1, 0, 0, 1)); //redquad.addColor(ofDefaultColorType(0, 1, 0, 1)); //greenquad.addColor(ofDefaultColorType(0, 0, 1, 1)); //bluequad.addColor(ofDefaultColorType(1, 1, 1, 1)); //white// 按照逆时针顺序为四个顶点分配UV坐标quad.addTexCoord(glm::vec2(0, 0));  // 左下角quad.addTexCoord(glm::vec2(0, 1));  // 左上角quad.addTexCoord(glm::vec2(1, 1));  // 右上角quad.addTexCoord(glm::vec2(1, 0));  // 右下角ofIndexType indices[6] = { 0,1,2,2,3,0 };quad.addIndices(indices, 6);shader.load("passthrough.vert", "uv_vis.frag");
}//--------------------------------------------------------------
void ofApp::update(){}//--------------------------------------------------------------
void ofApp::draw(){shader.begin();quad.draw();shader.end();}//--------------------------------------------------------------
void ofApp::keyPressed(int key){}//--------------------------------------------------------------
void ofApp::keyReleased(int key){}//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){}//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){}//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){}//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){}//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){}//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){}//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){}//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){}//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){}
main.cpp
#include "ofMain.h"
#include "ofApp.h"//========================================================================
int main() {ofGLWindowSettings glSettings;glSettings.setSize(1024, 768); //was 748 verticalglSettings.windowMode = OF_WINDOW;glSettings.setGLVersion(4, 1);ofCreateWindow(glSettings);printf("%s\n", glGetString(GL_VERSION));ofRunApp(new ofApp());}
passthrough.vert
#version 410layout (location = 0) in vec3 pos; 
layout (location = 3) in vec2 uv;out vec2 fragUV;void main()
{gl_Position = vec4(pos, 1.0);fragUV = uv;
}
uv_vis.frag
#version 410in vec2 fragUV;
out vec4 outCol;void main()
{outCol = vec4(fragUV, 0.0f, 1.0f);
}
http://www.dtcms.com/a/342770.html

相关文章:

  • 【python】windows下使用pyenv+uv进行python版本及环境变量管理
  • K 均值聚类(K-Means)演示,通过生成笑脸和爱心两种形状的模拟数据,展示了无监督学习中聚类算法的效果。以下是详细讲解:
  • 微服务02-Spring Cloud入门:构建微服务生态系统
  • 灵活使用UE5 Modeling中的UV编辑功能
  • RabbitMQ死信队列、延时队列分别是什么
  • 常德二院全栈国产化信创项目:开启医疗新质生产力的“头雁”之旅
  • 【STM32】HAL库中的实现(九):SPI(串行外设接口)
  • 如何在阿里云OSS之间进行数据迁移呢?
  • Pytorch安装详细步骤
  • Navicat16.3.9 连接 MongoDB 数据库异常及解决
  • 【CSP初赛】程序阅读15
  • 【C++】类和对象——默认成员函数(中)(附思维导图)
  • 算力魔方迷你主机的“八爪鱼”模式
  • 扣子Coze教程:自动化拆解小红书对标账号,输出完整分析报告(附MCP配置)
  • 亚马逊意大利保证金新政深度解析:合规挑战与跨境运营策略重构
  • MySql 特殊函数
  • Redisson相关知识
  • 数据结构青铜到王者第一话---数据结构基本常识(1)
  • 零基础从头教学Linux(Day 17)
  • 在职老D渗透日记day23:sqli-labs靶场通关(第29关-31关)http参数过滤
  • [软件开发技术栈]从MVVM到MVC
  • 大模型提示词工程背后的原理:深入理解Prompt Learning(提示学习)
  • 【Dify(v1.x) 核心源码深入解析】prompt 模块
  • 单智能体篇:Prompt工程艺术
  • 【运维进阶】Shell 函数的知识与实践
  • CTFSHOW | 其他篇题解(一)web396-web416
  • 学习日志39 python
  • 华为iVS1800接入SVMSPro平台
  • Web3 的发展挑战:技术、监管与生态的多重困境
  • 使用C++11改进工厂方法模式:支持运行时配置的增强实现