我的博客和笔记我的博客和笔记
首页
文章
  • TurboLink
  • TinyEncrypt
  • UnrealStyleGuide
  • AxTrace
  • Cyclone
  • 数学相关
  • 图形学
  • 密码学
  • 编程语言
关于
GitHub
首页
文章
  • TurboLink
  • TinyEncrypt
  • UnrealStyleGuide
  • AxTrace
  • Cyclone
  • 数学相关
  • 图形学
  • 密码学
  • 编程语言
关于
GitHub
  • 我的文章

    • 从抛币协议到智能合约

      • Part1
      • Part2
    • JPEG算法解密

      • Part1
      • Part2
      • Part3
      • Part4
      • Part5
      • Github
    • SPH算法简介

      • Part1
      • Part2
      • Part3
      • Part4
      • Github
    • 赌博中的数学:Martingle策略
    • 如何生成一个随机的圆形
    • 一个简单的DH密钥协商算法的实现
    • 如何计算线段和圆的交点
    • 一道数学趣题
    • 斐波那契数列和1/89
    • 匀速贝塞尔曲线运动的实现

      • Part1
      • Part2
  • 开源项目

    • TurboLink
    • TinyEncrypt
    • UnrealStyleGuide
    • AxTrace
    • Cyclone
  • 学习笔记

    • 数学相关

      • 常用数学符号
      • 群
      • 数论(一)
      • 数论(二)
      • 数论(三)
      • 概率
    • 密码学

      • RSA
      • 抛币协议
      • 智能扑克协议
    • 图形学

      • 数学基础

        • 矢量
        • 矩阵
        • 立体角
        • 几何变换(一)
        • 几何变换(二)
        • 法线变换
        • 摄像机变换
      • 光照模型

        • 传统光照模型
        • 光度学
        • 双向反射分布函数(BRDF)
        • 微平面理论(一)
        • 微平面理论(二)
        • 微平面理论(三)
        • 光照方程
      • 环境光渲染

        • 环境光渲染(一)
          • 1. 定义
          • 2.漫反射部分
            • 2.1 原理
            • 2.2 漫反射环境贴图的生成
        • 环境光渲染(二)
    • 编程语言

      • JavaScript

        • 环境搭建
        • 基本语法
        • 函数
        • 对象和类

环境光渲染(一)


1. 定义

使用一张HDR环境图片作为环境光源,每个像素表示一个光源和入射方向,回顾一下渲染方程

Lo(ωo→)=∫Ω(kdcπ+fs(ωi→,ωo→))Li(ωi→)(ω→in→)dωi

显然在实时渲染系统中,针对环境贴图做完整的积分运算代价非常昂贵,所以需要做预处理

2.漫反射部分

2.1 原理

只考虑渲染方程中的漫反射部分

Lod(ωo→)=kdcπ∫ΩLi(ωi→)(ω→in→)dωi

将其中之和光照贴图相关的部分做一个预计算,针对所有球面方向,每个方向做一个半球积分

1π∫ΩLi(ωi→)(ω→in→)dωi

将结果存成一张新的环境贴图Irradiance,那么最终实时渲染时,漫反射部分可以这个贴图来计算

Lod(ωo→)=TexCube(IrrMap,n→)∗kd∗c

2.2 漫反射环境贴图的生成

2.2.1 球坐标积分

将立体角转换为球坐标

1π∫ΩLi(ωi→)(ω→in→)dωi=1π∫ϕ=02π∫θ=0π/2Li(ϕ,θ)cos⁡(θ)sin⁡(θ)dθdϕ≈1π2πn1π2n2∑j=0n1∑i=0n2Li(ϕj,θi)cos⁡(θi)sin⁡(θi)=πn1n2∑j=0n1∑i=0n2Li(ϕj,θi)cos⁡(θi)sin⁡(θi)

其中:

ϕj=jn12πθi=in2π2

代码:

vec3 irradiance = vec3(0.0);  

// tangent space calculation from origin point(x'=up, y'=N, z'=left)(left-hand)
vec3 up    = vec3(0.0, 1.0, 0.0);   //x'
vec3 left = cross(up, N);			//z'
up         = cross(N, left);		//x'

float sampleDelta = 0.025;
float nrSamples = 0.0; 
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
{
    for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
    {
        float cosTheta = cos(theta);
        float sinTheta = sin(theta);
    
        // spherical to cartesian (in tangent space)
        vec3 tangentSample = vec3(cos(phi) * sinTheta, cosTheta, sin(phi) * sinTheta);
        // tangent space to world
        vec3 sampleVec = tangentSample.x * up + tangentSample.y * N + tangentSample.z * left; 

        irradiance += texture(environmentMap, sampleVec).rgb * cosTheta * sinTheta;
        nrSamples++;
    }
}
irradiance = PI * irradiance * float(nrSamples);

2.2.2 蒙特卡洛估算

使用蒙特卡洛采样估算积分

∫abf(x)dx≈1N∑i=1Nf(Xi)pdf(Xi)

其中pdf(x)函数是采样时使用的概率分布函数。 方法1: 对于要计算的这个二维积分,如果使用的随机采样是ϕ在[0,2π]之间均匀分布,θ在[0,π/2]之间均匀分布,那么

pdf(ϕ)=1/(2π),pdf(θ)=2/π1π∫ϕ=02π∫θ=0π/2Li(ϕ,θ)cos⁡(θ)sin⁡(θ)dθdϕ=1π2πn1∑j=0n1(π2n2∑i=0n2Li(ϕj,θi)cos⁡(θi)sin⁡(θi))=πn1n2∑j=0n1∑i=0n2Li(ϕj,θi)cos⁡(θi)sin⁡(θi)=πN∑i=0NLi(ϕi,θi)cos⁡(θi)sin⁡(θi)

其中

ϕi=2πξθi=π2ξ

ξ表示均匀分布在[0,1]之间的随机变量

方法2: 上面的采样方法在极点位置会比较密集,在赤道位置比较稀疏,由于漫反射是均匀分布的,如果采样点也是均匀分布在半球面上的,收敛速度会比较快,这种情况下

pdf(ϕ)=1/(2π),pdf(θ)=sin⁡(θ)1π∫ϕ=02π∫θ=0π/2Li(ϕ,θ)cos⁡(θ)sin⁡(θ)dθdϕ=1π2πn1∑j=0n1(1n2∑i=0n2Li(ϕj,θi)cos⁡(θi))=2n1n2∑j=0n1∑i=0n2Li(ϕj,θi)cos⁡(θi)=2N∑i=0NLi(ϕi,θi)cos⁡(θi)

其中

ϕi=2πξθi=arccos⁡(1−ξ)
vec3 irradiance = vec3(0.0);  

vec3 up = vec3(0.0, 1.0, 0.0); //x'
vec3 left = cross(up, N);	//z'
up = cross(N, left);	//x'

for(uint i=0; i<sampleCounts; i++)
{
    //http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
    vec2 uv = Hammersley(i, totalCounts);

    // tangent space sample point
    float phi = uv.y * 2.0 * PI;
    float cosTheta = 1.0 - uv.x;
    float sinTheta = sqrt(1-cosTheta*cosTheta);
    vec3 tangentSample = vec3(cos(phi) * sinTheta, cosTheta, sin(phi) * sinTheta);

    // tangent space to world
    vec3 sampleVec = tangentSample.x * up + tangentSample.y * N + tangentSample.z * left; 

    irradiance += texture2D(s_texSkybox, sampleVec).rgb * cosTheta;
}

irradiance = 2*irradiance / float(sampleCounts);
Next
环境光渲染(二)