图形学基础1

2023-11-17

坐标系相关

uv可能会影响局部坐标系,如果light图和brdf图做卷积的时候,局部坐标系保持一致很重要
如下图:tangent是从外部模型文件进行加载的
在这里插入图片描述
在这里插入图片描述

切线空间采样并转世界坐标系

// spherical to cartesian (in tangent space)
vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
// tangent space to world
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;

 其中theta、phi是切线空间极坐标系,是随机采样出来的;然后转为直角坐标系,这里不需要考虑直角坐标系x,y,z的顺序,但在从切线空间转世界空间时,cos(theta)要和 N 对应上。

 对于up right可以这样生成,它们怎么样生成都可以,只要和N向量构成两两垂直即可,但如果up向量和N向量相同的话,做cross可能会有一定风险(cross出来是0向量):

vec3 up    = vec3(0.0, 1.0, 0.0);
vec3 right = normalize(cross(up, N));
up         = normalize(cross(N, right));

 Games101作业转换坐标系是这么做的:
 里面的B,C向量相当于up,right,主要学习里面C向量的设计,这样设计可以使得C向量不会为0向量且一定垂直于N。

//切线直接坐标的a向量转换到世界空间
    Vector3f toWorld(const Vector3f &a, const Vector3f &N){
        Vector3f B, C;
        //这里的条件判断,应该是为了避免出现C向量为0向量的情况
        if (std::fabs(N.x) > std::fabs(N.y)){//至少在x轴上有分量
            float invLen = 1.0f / std::sqrt(N.x * N.x + N.z * N.z);
  			//C和N点乘为0,相互垂直;C向量不为0
            C = Vector3f(N.z * invLen, 0.0f, -N.x *invLen);
            
        }
        else {//至少在y或z方向上有分量
            float invLen = 1.0f / std::sqrt(N.y * N.y + N.z * N.z);
            C = Vector3f(0.0f, N.z * invLen, -N.y *invLen);
        }
        B = crossProduct(C, N);
        return a.x * B + a.y * C + a.z * N;
    }
...


在ssao中有对up和right做扰动来增加采样的随机性,其中生成了扰动向量randomVec,然后做施密特正交化过程,使得randomVec垂直于N。

vec3 randomVec = texture(texNoise, TexCoords * noiseScale).xyz;
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));//施密特正交化过程,使得tangent必与normal垂直
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);

assimp中的tangent与bitangent

在这里插入图片描述
有texcoord一般也默认带上了tangent和bitangent

Falcor中获取垂直向量

// Utility function to get a vector perpendicular to an input vector 
//    (from "Efficient Construction of Perpendicular Vectors Without Branching")
float3 getPerpendicularVector(float3 u)
{
	float3 a = abs(u);
	uint xm = ((a.x - a.y)<0 && (a.x - a.z)<0) ? 1 : 0;
	uint ym = (a.y - a.z)<0 ? (1 ^ xm) : 0;
	uint zm = 1 ^ (xm | ym);
	return cross(u, float3(xm, ym, zm));
}

后续 Falcor半球cosine采样以及均匀采样 有用到

随机数

CPU

inline float get_random_float()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_real_distribution<float> dist(0.f, 1.f);
    return dist(rng);
}

GPU

1.低差异序列
在这里插入图片描述

 设总样本数为 N,样本索引为 i

// ----------------------------------------------------------------------------
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// efficient VanDerCorpus calculation.
float RadicalInverse_VdC(uint bits) 
{
     bits = (bits << 16u) | (bits >> 16u);
     bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
     bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
     bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
     bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
     return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------
vec2 Hammersley(uint i, uint N)
{
	return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}

用法:
vec2 Xi = Hammersley(i, SAMPLE_COUNT);

Falcor中使用的random(GPU)

  1. 在raygen中根据FrameCount和pixelId去initRand
// Generates a seed for a random number generator from 2 inputs plus a backoff
uint initRand(uint val0, uint val1, uint backoff = 16)
{
	uint v0 = val0, v1 = val1, s0 = 0;

	[unroll]
	for (uint n = 0; n < backoff; n++)
	{
		s0 += 0x9e3779b9;
		v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
		v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
	}
	return v0;
}

使用如下:

// Initialize a random seed, per-pixel, based on a screen position and temporally varying count
uint randSeed = initRand(launchIndex.x + launchIndex.y * launchDim.x, gFrameCount, 16);
  1. 假设需要在cosHemiSphere上进行采样
for (int i = 0; i < gNumRays; i++)
{
	// Sample cosine-weighted hemisphere around surface normal to pick a random ray direction
	float3 worldDir = getCosHemisphereSample(randSeed, worldNorm.xyz);
	// Shoot our ambient occlusion ray and update the value we'll output with the result
	// 因为不是 蒙特卡洛,因此这里的cos采样 不需要考虑 概率
	ambientOcclusion += shootAmbientOcclusionRay(worldPos.xyz, worldDir, gMinT, gAORadius);
}

getCosHemisphereSample函数定义,注其返回的是世界坐标系下的vector:

// Get a cosine-weighted random vector centered around a specified normal direction.
float3 getCosHemisphereSample(inout uint randSeed, float3 hitNorm)
{
	// Get 2 random numbers to select our sample with
	float2 randVal = float2(nextRand(randSeed), nextRand(randSeed));

	// Cosine weighted hemisphere sample from RNG
	float3 bitangent = getPerpendicularVector(hitNorm);
	float3 tangent = cross(bitangent, hitNorm);
	float r = sqrt(randVal.x);
	float phi = 2.0f * 3.14159265f * randVal.y;

	// Get our cosine-weighted hemisphere lobe sample direction
	return tangent * (r * cos(phi).x) + bitangent * (r * sin(phi)) + hitNorm.xyz * sqrt(1 - randVal.x);
}

其中nextRand定义如下:其返回的是一个[0…1]的随机数,同时也更新随机数种子

// Takes our seed, updates it, and returns a pseudorandom float in [0..1]
float nextRand(inout uint s)
{
	s = (1664525u * s + 1013904223u);
	return float(s & 0x00FFFFFF) / float(0x01000000);
}

采样

半球内均匀采样(CPU)

Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
    switch(m_type){
        case DIFFUSE:
        {
            // uniform sample on the hemisphere
            float x_1 = get_random_float(), x_2 = get_random_float();
            float z = std::fabs(1.0f - 2.0f * x_1);
            float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
            Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);
            return toWorld(localRay, N);
            
            break;
        }
    }
}

GGX重要性采样(GPU)

参考:https://blog.csdn.net/weixin_43803133/article/details/110385305
在这里插入图片描述
在这里插入图片描述

vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)//输入Xi随机数以及法线N,在tangent space的半球上随机获取一个向量,并返回到世界坐标系
{
	float a = roughness*roughness;
	
	float phi = 2.0 * PI * Xi.x;
	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
	float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
	
	// from spherical coordinates to cartesian coordinates - halfway vector
	vec3 H;
	H.x = cos(phi) * sinTheta;
	H.y = sin(phi) * sinTheta;
	H.z = cosTheta;
	
	// from tangent-space H vector to world-space sample vector
	vec3 up        = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
    vec3 tangent   = normalize(cross(up, N));
	vec3 bitangent = cross(N, tangent);
	
    vec3 sampleVec = bitangent * H.x + tangent * H.y + N * H.z;

	return normalize(sampleVec);
}

使用如下:用于生成镜面朝向。

for(uint i = 0u; i < SAMPLE_COUNT; ++i)
    {
        // generates a sample vector that's biased towards the preferred alignment direction (importance sampling).
        vec2 Xi = Hammersley(i, SAMPLE_COUNT);
        vec3 H = ImportanceSampleGGX(Xi, N, roughness);
        ...

Falcor半球cosine采样以及均匀采样

注意这些都返回的是世界坐标系下的vector:
cosine 采样:

// Get a cosine-weighted random vector centered around a specified normal direction.
float3 getCosHemisphereSample(inout uint randSeed, float3 hitNorm)
{
	// Get 2 random numbers to select our sample with
	float2 randVal = float2(nextRand(randSeed), nextRand(randSeed));

	// Cosine weighted hemisphere sample from RNG
	float3 bitangent = getPerpendicularVector(hitNorm);
	float3 tangent = cross(bitangent, hitNorm);
	float r = sqrt(randVal.x);
	float phi = 2.0f * 3.14159265f * randVal.y;

	// Get our cosine-weighted hemisphere lobe sample direction
	return tangent * (r * cos(phi).x) + bitangent * (r * sin(phi)) + hitNorm.xyz * sqrt(1 - randVal.x);
}

对应的概率:NdotL / M_PI

均匀采样:

// Get a uniform weighted random vector centered around a specified normal direction.
float3 getUniformHemisphereSample(inout uint randSeed, float3 hitNorm)
{
	// Get 2 random numbers to select our sample with
	float2 randVal = float2(nextRand(randSeed), nextRand(randSeed));

	// Cosine weighted hemisphere sample from RNG
	float3 bitangent = getPerpendicularVector(hitNorm);
	float3 tangent = cross(bitangent, hitNorm);
	float r = sqrt(max(0.0f,1.0f - randVal.x*randVal.x));
	float phi = 2.0f * 3.14159265f * randVal.y;

	// Get our cosine-weighted hemisphere lobe sample direction
	return tangent * (r * cos(phi).x) + bitangent * (r * sin(phi)) + hitNorm.xyz * randVal.x;
}

对应的概率:1.0f / (2.0f * M_PI)

使用如下:

if (gCosSampling)
	bounceDir = getCosHemisphereSample(randSeed, worldNorm.xyz);      // Use cosine sampling
else
	bounceDir = getUniformHemisphereSample(randSeed, worldNorm.xyz);  // Use uniform random samples
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图形学基础1 的相关文章

  • Vulkan下多线程渲染设计

    1 Vulkan 视角下的多线程渲染 首先我们需要从vulkan api的顶层框架上来看一下 它在哪些地方可以让我们并行 Vulkan API的基本框架 Vulkan不同于Gles只有一个 不被API暴露出来的 单一链条的cmdbuffer
  • 球谐函数原理

    参考 球谐函数极其应用 球谐函数是什么 球谐函数是傅里叶级数的高维类比 由一组表示球体表面的基函数构成 同时 球谐函数是Laplace算子角动量在三个维度上的特征函数 文章目录 球谐函数是什么 球坐标下的Laplace方程 Laplace方
  • 人像美妆效果实现(贴纸)——OpenGL实现

    一 贴纸美妆 人像美妆效果的实现方式有很多种 其中贴纸美妆是最常见也是应用最广泛的一种实现方式 它有实现简单 素材可移植和可复用性高等优点 是目前众多美颜美妆相关的App中应用最多的美妆实现方式 它有点像我们玩的贴在脸上或手臂上的的纹身贴纸
  • PRT(Precomputed Radiance Transfer【2002】)原理实现

    声明 本文源自对Games202课程 作业2的总结 参考 手把手教你写GAMES202作业 GAMES202 作业2 Precomputed Radiance Transfer 球谐函数 GAMES 202 作业2 Games202课程 个
  • Vulkan入门精要

    Vulkan入门精要 fuxiii github io https fuxiii github io Essentials of Vulkan
  • 次表面散射

    技术博客 https github com Li ZhuoHang Subsurface scattering 基于屏幕空间模糊的次表面散射 SSSSS 效果展示 原模型 开启镜面表面反射 float PHBeckmann float nd
  • [游戏开发][Shader]GLSLSandBox转Unity-CG语言

    官网 GLSL Sandbox Galleryhttps glslsandbox com 屏幕坐标计算 fragCoord与 ScreenParams mat2矩阵转换 vec2向量 在GLSL里mat2 a b c d 函数返回vec2
  • 在vue中使用antV-G2展示柱状图

    介绍 G2 是一套基于图形语法理论的可视化底层引擎 以数据驱动 提供图形语法与交互语法 具有高度的易用性和扩展性 使用 G2 你可以无需关注图表各种繁琐的实现细节 一条语句即可使用 Canvas 或 SVG 构建出各种各样的可交互的统计图表
  • 用云渲染好还是自己搭建传统渲染农场好?

    今天云渲染小编就和大家说说云渲染以及它和传统渲染农场的区别 以及用云渲染好还是自己搭建传统渲染农场好 一 什么是云渲染 云渲染什么意思 首先云渲染是一种依托于云计算的云端服务 用户将本地文件提交到云端服务器 云端计算机集群完成渲染 再将渲染
  • WebGL笔记 (侧重理论基础向)

    要把Cesium three js 这些玩明白还是要有WebGL的知识的 不然只是官方demo的ctrl cv侠 本笔记参考的教程 2022年WebGL入门教程 完结 哔哩哔哩 bilibili 一 初级 二维 1 1 坐标系 WebGL的
  • Hello Vulkan(五)

    上一期技术分享中 我们讲述了如何关于Vulkan的Data Buffers使用及VMA内存管理器使用 自己创建或使用VMA在显存里创建Buffers 并在CPU进行读取或写入 本期将继续分享关于Vulkan的技术 内容是非常有趣的部分 即
  • 初识OpenGL (-)纹理过滤(Texture Filtering)

    1 OpenGL需要知道怎样将纹理像素 Texture Pixel 也叫Texel 映射到纹理坐标 纹理坐标 不依赖于分辨率 Resolution 它可以是任意浮点值 给模型顶点设置的那个数组 OpenGL以这个顶点的纹理坐标数据去查找纹理
  • 【机器人仿真Webots教程】-Webots安装

    Webots安装 文章目录 Webots安装 1 Webots简介 2 Webots安装 2 1 系统要求 2 2 验证显卡驱动 2 3 安装 3 Webots仿真 3 1 world文件 3 2 Controller文件 3 3 Supe
  • DirectX12 3D游戏开发实践(龙书)第四章 Direct3D的初始化

    目录 Direct3D的初始化 预备知识 Direct3D概述 组件对象模型 纹理格式 Textures Formats 交换链和页面翻转 深度缓冲 资源与描述符 多重采样技术的原理 利用Direct3D进行多重采样 功能级别 Direct
  • 计算机图形学GAMES101(十五)光线追踪(蒙特卡洛积分与路径追踪)

    本节涉及内容 蒙特卡罗积分 路径追踪 蒙特卡罗积分 蒙特卡罗积分的核心思想还是求一个不规则图形的面积 它的做法是 首先在a和b之间找一个值xi然后求f x 接着以f x 为高 ab为宽求矩形的面积 最后将所有的值求平均 当采样数量xi趋于无
  • 3D图形渲染技术

    如何用2D平面展现3D图形 2D图形 在一个平面中有了两个点 知道了他们的XY坐标 就可以把它们链接起来画成一条线 通过控制A和B点的XY坐标可以控制一条线 在3D图像中 点的坐标多了一个Z轴的坐标系 但是在2D的屏幕坐标上不可能有XYZ立
  • OpenGL 入门 10:光源

    点光源 点光源的强度需要随着距离增加而减少 至于减少的系数公式大致如下 在这里d代表了片段距光源的距离 接下来为了计算衰减值 我们定义3个 可配置的 项 常数项Kc 一次项Kl和二次项Kq 常数项通常保持为1 0 它的主要作用是保证分母永远
  • Vulkan 绘制显示设计

    背景 众所周知 Vulkan是个跨平台的图形渲染API 为了友好地支持跨平台 Vulkan自然也抽象出了很多接口层去对接各个操作系统 抹平系统间的差异 Swap Chains即为WSI 其本质上是一种图像队列 此队列会按顺序依次将队列中的若
  • OSG中几何体的绘制(二)

    5 几何体操作 在本章的前言中就讲到 场景都是由基本的绘图基元构成的 基本的绘图基元构成简单的几何体 简单的几何体构成复杂的几何体 复杂的几何体最终构造成复杂的场景 当多个几何体组合时 可能存在多种降低场景渲染效率的原因 在很多3D引擎中
  • OSG中几何体的绘制(一)

    本章主要介绍一些几何体的绘制方法 绘制几何体在场景中是非常常见的 也是最基本的 在很多应用程序中可以看到相当复杂的场景 但不管场景有多复杂 它们都是由少数几个基本的图形元素构建而成的 只要想想达芬奇那些伟大的作品也是由铅笔和画刷所完成的 读

随机推荐

  • Sklearn GridSearchCV跑SVM很慢或卡死解决办法,SVM线性核函数卡死

    今天跑人工智能SVM实验 想试一下线性核函数 结果卡死了 很久也不出结果 但之前使用高斯核函数是没问题的 历经千辛万苦终于找到了原因 记录一下 希望对后人有帮助 本人只是个做作业的小菜菜 如有不对欢迎指正 参考了以下文章 关于Python
  • 集成模型(2)GBDT用于分类和回归及其python实现

    GBDT用于分类和回归及其python实现 1 GBDT回归 1 1基本思想 1 2算法流程 2 GBDT二分类 2 1基本思想 2 2算法流程 2 3python实现 2 3 1回归树 2 3 2GBDT实现 adaboost用于分类的时
  • Windows下的脚本在Linux中运行乱码问题

    Windows下的py脚本在Linux中运行 dos下写的代码 拿到linux下 存在不兼容 解决办法 方法一 dos2unix 如果没有该插件 需要安装 sudo apt get install dos2unix dos2unix she
  • 修改html代码页面没有变化,VUE 直接通过JS 修改html对象的值导致没有更新到数据中解决方法分析...

    本文实例讲述了VUE 直接通过JS 修改html对象的值导致没有更新到数据中解决方法 分享给大家供大家参考 具体如下 业务场景 我们在使用vue 编写 代码时 我们有一个 多行文本框控件 希望在页面点击一个按钮 在 文本框焦点位置插入一个
  • 星星之火-32: 扩频码、OVSF正交扩频码、Walsh函数、信道码;扰码、伪随机码序列、m序列码、Gold码序列

    1 OVSF正交扩频码 OVSF是Orthogonal Variable Spreading Factor的缩写 叫正交可变扩频因子 系统根据扩频因子的大小给用户分配资源 数值越大 提供的带宽越小 2 Walsh函数 是J L Wash于1
  • 使用 exceljs 导出表格有下拉框(浏览器,客户端)

    exceljs服务端导出表格 浏览器直接用不了 nodejs的运行环境和浏览器的不同 exceljs min js 下载地址 https www bootcdn cn exceljs git的exceljs里面没有这个文件 代码和nodej
  • Linux之常用命令解压缩

    总结一下Linux的解压缩命令 包括tar zip 下面是一些常用的解压缩命令 1 解压缩 tar 文件 解压缩 tar 文件 tar xvf 文件名 tar 解压缩 tar gz 或 tgz 文件 tar xzvf 文件名 tar gz
  • 微信H5支付

    微信支付流程 大概就是这个样子 也包括支付宝 各大银联差不多一个流程 也就是传递的参数略有不同 用户通过客户端下一个订单 后台根据用户下的商品来生成一个订单 然后可以有一个订单确认页面 以显示订单是否完整 之后确认订单 把订单信息发送给微信
  • 有discuz数据库,忘了管理员密码,怎样进后台

    很简单 你注册一个用户 密码设个简单一点的 然后在ucenter的数据库中uc members表中找到这个用户的password字段和salt字段 把你在uc members表中的管理员账号的password和salt字段修改成新注册用的的
  • rename()函数

    rename 函数 可以直接通过columns 去传入对应列的名字 去改变列名 这种效率比rename的效率要高的多 score columns python java ps js rename 函数的参数 mapper None axis
  • 对上拉下拉电阻的作用的总结

    一 定义 上拉就是将不确定的信号通过一个电阻嵌位在高电平 电阻同时起限流作用 下拉同理 上拉是对器件注入电流 下拉是输出电流 弱强只是上拉电阻的阻值不同 没有什么严格区分 对于非集电极 或漏极 开路输出型电路 如普通门电路 提升电流和电压的
  • 掌握shell编程中的细节:轻松应对可能出现的考点(括号篇)

    前言 只要是shell脚本基本都会用这几个符号 重点是你会不会口语描述其功能 下面是对shell里各种括号的实际的演示和功能的描述 小括号or单小括号 功能 命令的集合 括号内的命令会在一个子shell中按顺序执行 括号内的写的变量不会被括
  • docker镜像详解

    目录 什么是docker镜像 镜像相关命令 docker pull docker images docker search docker rmi 导出 导入镜像 镜像分层 镜像摘要 镜像摘要的作用 分发散列值 什么是docker镜像 Doc
  • 帆软填报界面首页黑色

    解决方法 左上角 gt 模板 gt 模板web属性 gt 填报的话 选填报界面 gt 为该模板单独设置 gt 左上角 填报当前行背景颜色 gt 改成 白色 或其他
  • 统计字符串中每个单词出现的个数和频率----四种方法

    统计每个单词出现的个数 三种方法 第一种如下 最简单的方式 sentance I can because i think i can 切片分隔成列表序列 用列表推导式表达 rresult word sentance split count
  • 认识数据中心两个关键指标RTO和RPO

    RTO和RPO是Business Continuity BC and Disaster Recovery DR 里面两个重要的概念 也是类似产品的Service Level Agreement SLA 的两个重要的衡量指标 Recovery
  • 观察者模式和事件通知备忘

    观察者模式和事件通知备忘 MessageBus instance post Notify PARKIN in bytes 这种是仿照Android的EventBus 用new的一个实例对象根据path反射调用其中的方法处理逻辑 要修改为 r
  • 【JVM】如何通俗地讲解JVM各个组成部分和其基本功能?

    类加载器 ClassLoader 运行时数据区 Runtime Data Area 执行引擎 Execution Engine 本地库接口 Native Interface 组件的作用 首先通过类加载器 ClassLoader 会把 Jav
  • (服务计算)在centos上编写golang的库,并进行测试

    首先是在centos上按照老师给的教程安装golang的相关内容 安装成功后进行后面的操作 首先是创建了一个hello go的文件 然后执行结果如下 可知安装基本正确 然后编写第一个库 首先创建包路径 然后创建名为reverse go的文件
  • 图形学基础1

    坐标系相关 uv可能会影响局部坐标系 如果light图和brdf图做卷积的时候 局部坐标系保持一致很重要 如下图 tangent是从外部模型文件进行加载的 切线空间采样并转世界坐标系 spherical to cartesian in ta