GAMES-101-个人总结归纳-Shading

(注:此文以个人知识框架为基础拓开,为方便后期回顾,在保留原课程内容框架的基础上,个别概念的介绍可能过于简单,感兴趣读者可到 GAMES 平台深入学习)

目录

概要

Illumination & Shading

Blinn-Phong Reflectance Model(Blinn-Phong 反射模型)

Diffuse Reflection(漫反射项)

Specular term(高光项)

Ambient Term(环境光项)

 Shading Frequencies(着色频率):

Graphics Pipeline

Texture Mapping(纹理映射)

Barycentric coordinates(重心坐标)

Texture queries(纹理查值)

纹理精度太小(多个像素对应一个纹理值)

方案:Bilinear Interpolation(双线性插值)

纹理精度太大(多个纹理值对应一个像素)

方案一:MipMap(方形范围查询)

方案二:Anisotropic Filtering(各向异性过滤:矩形范围查询)

方案三:EWA filtering(Elliptically Weighted Average:椭圆加权平均)

Applications of textures

定义纹理

 Environmental Lighting(环境光)

Bump/Normal Mapping(凹凸/法线贴图)

Displacement mapping(位移贴图)

作业三

题目简单介绍

核心代码

效果


概要

定义:The process of applying a material to an object(对物体应用材质的过程)

Illumination & Shading

Blinn-Phong Reflectance Model(Blinn-Phong 反射模型)

Diffuse Reflection(漫反射项)

  1. 漫反射和观察方向 v 无关。
  2. 关于公式的解释:

        kd:漫反射系数,不同材质kd不同,也可理解为颜色;

        (I/r^2):I(Intensity)光照强度;r:光线传播球面半径,即是光源到观测点的距离,此项表示I与r^2(球面半径)成反比;

        max(0,n.dot(l)):光线在物体表面法线方向上的分量(点乘算余弦),不能为负方向(光线从物体下表面打到观测点无意义);

Specular term(高光项)

        1.各项参数参考 diffuse,区别:犹豫半程向量 h 的引入,高光效果和观察方向 v ,光线方向 I有关系。

        2.指数p:控制高光范围大小(p越大高光越小)。

Ambient Term(环境光项)

 Shading Frequencies(着色频率):

        Flat shading:逐三角形(逐平面)着色

        Gouraud shading:逐顶点着色

        Phong shading:逐像素着色

Graphics Pipeline

 

Texture Mapping(纹理映射)

定义:在一个二维平面上定义三维物体表面每一个点的基本属性。

手段:定义每一个小三角形每一个顶点对应纹理上坐标(UV)上的位置。

Barycentric coordinates(重心坐标)

定义:

1.三角形平面内的任意点,可通过不同系数的三顶点和的方式表达,系数均非负,表示点在三角形内。

2.各系数的值为:该点与三角形任意两点的面积 与 原三角形面积的比值。公共边对应的原三角形顶点即为该系数对应的顶点。(如下图:系数 beta 对应面积 AB 对应边 AC 对应顶点 B)

 3.通用公式:

目的:在三角形内部进行线性插值,给定三顶点颜色、法线向量、纹理坐标的情况下,三角形内部点平滑过渡效果

Texture queries(纹理查值)

纹理精度太小(多个像素对应一个纹理值)

场景:纹理精度不够的情况

方案:Bilinear Interpolation(双线性插值)

取像素点对应纹理空间周围的四个点(u00,u01,u10,u11),按照水平和垂直方向上的比重计算平滑的过渡值。

效果对比:从左到右,直接取纹理;双线性插值;(不知如何翻译:取纹理空间周围16个点,原理同双线性插值);

 

纹理精度太大(多个纹理值对应一个像素)

(注:此小节涉及的所有所有形状(mipMap:适合方形;Anisotropic:适合矩形;EWA:对角线)指的是屏幕4个像素点映射到纹理空间后形成的大致形状,以mipmap 为例:下图4个点映射到纹理空间可近似成一个正方形)。

场景:透视投影远景的纹理值查询,直接查纹理会产生摩尔纹(远)和锯齿(近)。

方案一:MipMap(方形范围查询)

特性:对正方形(不是矩形)纹理做不准确快速范围查询。

定义:存储多层次纹理副本,根据实际精度直接查询不同层次副本

 图像金字塔,(存储开销增加1/3:1+1/4 + 1/16 + 1/64+...)

 怎么查?计算屏幕两个像素点(左)对应纹理空间(右)之间的的距离L,层次D = log2(L),也就是在纹理空间第几层边长为L(近似看成)正方形 会变成1个像素

不同层次之间的过渡问题:1.8层怎么查?

Trilinear Interpolation(三线性插值):对第1和第2层分别做双线性插值,将两个结果再做一次线性插值;

problem:对(坐标轴对齐)矩形对角线(也可看成是坐标轴不对齐的矩形)不友好

方案二:Anisotropic Filtering(各向异性过滤:矩形范围查询)

现代GPU在硬件层次支持各向异性过滤,即在原 texture(下图左上第一个) 的基础上进行横向和纵向(各项异指的就是横纵向不同)不同缩放比例的采样。最大支持 x16 级别各向异性,即横纵各16个样本(每个样本横 \ 纵向变为前一个1/2)。因此不会产生更多的内存开销。(关于开销,课程讲的是无限逼近原3倍,这是在不支持 GPU 采样的基础上)。

solve:坐标轴对齐的矩形查询;

problem:对角线查询;

other:课程中对此方案只做了简单介绍。关于如何查询等细节,有兴趣读者可参考知乎这篇文章

方案三:EWA filtering(Elliptically Weighted Average:椭圆加权平均)

此方案课程只简单提了下,说说我个人的理解:

  • 为什么是椭圆?椭圆是 bounding-box,我们知道如果是规则多边形,用圆做 bouding-box 可以实现最大覆盖率,EWA 方案主要为了处理不规则多边形问题,所以是椭圆。
  • 椭圆中心的 uv 坐标表示屏幕空间变化一个像素,纹理空间 uv 的变化(当前像素的纹理坐标对于屏幕空间坐标的微分),椭圆的长轴和短轴的比例由实际的微分数值大小确定,这样可以精确反应出(屏幕空间)映射到纹理空间后 uv 的变化(可理解为基于椭圆的各向异性)。
  • 加权平均?对映射到纹理空间后的在椭圆包围盒内的点做加权平均。不同范围的结果值存储在不同的椭圆环中,方便做加速。
  • 查询?由椭圆中心由内而外多次查询,直到椭圆包围了纹理空间的所有采样点。
  • other: 关于此方案,网络上的中文说明莫衷一是,有兴趣的且有耐心的同学可以参考这篇论文

Applications of textures

定义纹理

In modern GPUs texture = memory + range query (filtering)(现代 GPU 中,支持范围查询的内存数据)。

 Environmental Lighting(环境光)

Bump/Normal Mapping(凹凸/法线贴图)

  • 定义:定义某个点的相对高度/法线。模拟凹凸不平的效果(不改变几何信息)
  • (2D)切线向量 (1,dp), 根据旋转公式:法线向量(-dp, 1),归一化:n(p) = (-dp, 1).normalized();

  • (3D) n = (-dp/du, -dp/dv, 1).normalized();(注:n(p) = (0,0,1)为局部坐标系,计算完法向量后需要变回世界坐标系)

Displacement mapping(位移贴图)

  • 相比 bump map,对顶点高度做了实际移动(这意味着三角形要足够小)。
  • other:directX 提供了一种动态细分(三角形)的技术:开始不需要一个足够细的模型,根据需要判断是否需要做三角形的细分。

作业三

作业三链接

题目简单介绍

在作业2深度插值的基础上,实现法向量、颜色、纹理颜色的插值;加上投影变换矩阵,直接运行得到法向量实现结果;依次实现 Blinn-Phong 模型计算Fragment Color;Texture Shading Fragment Shader;Bump mapping.;displacement mapping;

核心代码

1.法向量、颜色、纹理颜色的插值

/// <summary>
/// Screen space rasterization
/// </summary>
/// <param name="t">triangle</param>
/// <param name="view_pos">view position</param>
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{
    auto v = t.toVector4();
    // compute the bounding box
    int max_x = ceil(MAX(v[0].x(), MAX(v[1].x(), v[2].x())));
    int max_y = ceil(MAX(v[0].y(), MAX(v[1].y(), v[2].y())));

    int min_x = floor(MIN(v[0].x(), MIN(v[1].x(), v[2].x())));
    int min_y = floor(MIN(v[0].y(), MIN(v[1].y(), v[2].y())));

    for (int x1 = min_x; x1 <= max_x; x1++) {
        for (int y1 = min_y; y1 <= max_y; y1++) {
            if (insideTriangle((float)x1 + 0.5, (float)y1 + 0.5, t.v)) {
                //计算三角形重心坐标
                auto [alpha, beta, gamma] = computeBarycentric2D(x1, y1, t.v);

                //透视校正插值
                float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                z_interpolated *= w_reciprocal;

                if (depth_buf[get_index(x1, y1)] > z_interpolated) {
                    
                    auto interpolated_color = interpolate(alpha,beta,gamma,t.color[0], t.color[1], t.color[2],1);
                    auto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2],1);
                    auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2],1);
                    //view_pos[]是三角形顶点在view space中的坐标,插值是为了还原在camera space中的坐标
                    //详见http://games-cn.org/forums/topic/zuoye3-interpolated_shadingcoords/
                    auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2],1);

                    fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
                    payload.view_pos = interpolated_shadingcoords;
                    //Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
                    auto pixel_color = fragment_shader(payload);

                    // 更新深度
                    depth_buf[get_index(x1, y1)] = z_interpolated;
                    // 更新color
                    set_pixel(Eigen::Vector2i(x1,y1), pixel_color);
                }
            }
        }
    }
}

2.blinn-phong 模型实现

Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = payload.color;
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    Eigen::Vector3f result_color = {0, 0, 0};
    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        // 除了光照 light.intensity 所有向量均按单位向量计算。
        Eigen::Vector3f light_direc = (light.position - point).normalized();
        Eigen::Vector3f vision_direc = (eye_pos - point).normalized();
        float r_pow2 = (light.position - point).dot(light.position - point);

        // diffuse
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r_pow2) * MAX(0.0f, normal.normalized().dot(light_direc));
        // specular
        Eigen::Vector3f h = (vision_direc + light_direc).normalized();//半程向量
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_pow2) * pow(MAX(0.0f, normal.normalized().dot(h)), p);
        // ambient
        Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);

        result_color += (Ld + Ls + La);
    }

    return result_color * 255.f;
}

3.Texture Shading Fragment Shader 实现

Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f return_color = {0, 0, 0};
    if (payload.texture)
    {
        // TODO: Get the texture value at the texture coordinates of the current fragment
        return_color = payload.texture->getColor(payload.tex_coords.x(),payload.tex_coords.y());
    }
    Eigen::Vector3f texture_color;
    texture_color << return_color.x(), return_color.y(), return_color.z();

    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = texture_color / 255.f;// 只需要将漫反射系数替换为 color
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = texture_color;
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    Eigen::Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        
        Eigen::Vector3f light_direc = (light.position - point).normalized();
        Eigen::Vector3f vision_direc = (eye_pos - point).normalized();
        float r_pow2 = (light.position - point).dot(light.position - point);

        // diffuse
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r_pow2) * MAX(0, normal.normalized().dot(light_direc));
        // specular
        Eigen::Vector3f h = (vision_direc + light_direc).normalized();//半程向量
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_pow2) * pow(MAX(0, normal.normalized().dot(h)), p);
        // ambient
        Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
        result_color += (Ld + Ls + La);
    }

    return result_color * 255.f;
}

4.Bump mapping 实现

Eigen::Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{ {20, 20, 20}, {500, 500, 500} };
    auto l2 = light{ {-20, 20, 0}, {500, 500, 500} };

    std::vector<light> lights = { l1, l2 };
    Eigen::Vector3f amb_light_intensity{ 10, 10, 10 };
    Eigen::Vector3f eye_pos{ 0, 0, 10 };

    float p = 150;

    Eigen::Vector3f color = payload.color;
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;


    float kh = 0.2, kn = 0.1;

    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    Vector3f t = Eigen::Vector3f(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    Vector3f b = normal.cross(t);
    Matrix3f TBN;
    TBN << t.x(), b.x(), normal.x(),
        t.y(), b.y(), normal.y(),
        t.z(), b.z(), normal.z();

    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;

    float dU = kh * kn * (payload.texture->getColor(u + 1 / w, v).norm() - payload.texture->getColor(u, v).norm());
    float dV = kh * kn * (payload.texture->getColor(u, v + 1 / h).norm() - payload.texture->getColor(u, v).norm());
    Vector3f ln(-dU, -dV, 1);
    normal = (TBN * ln).normalized();

    return normal * 255.f;
}

5.displacement mapping 实现

Eigen::Vector3f displacement_fragment_shader(const fragment_shader_payload& payload)
{
    
    Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
    Eigen::Vector3f kd = payload.color;
    Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);

    auto l1 = light{{20, 20, 20}, {500, 500, 500}};
    auto l2 = light{{-20, 20, 0}, {500, 500, 500}};

    std::vector<light> lights = {l1, l2};
    Eigen::Vector3f amb_light_intensity{10, 10, 10};
    Eigen::Vector3f eye_pos{0, 0, 10};

    float p = 150;

    Eigen::Vector3f color = payload.color; 
    Eigen::Vector3f point = payload.view_pos;
    Eigen::Vector3f normal = payload.normal;

    float kh = 0.2, kn = 0.1;

    float x = normal.x();
    float y = normal.y();
    float z = normal.z();
    Vector3f t = Eigen::Vector3f(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
    Vector3f b = normal.cross(t);
    Matrix3f TBN;
    TBN << t.x(), b.x(), normal.x(),
        t.y(), b.y(), normal.y(),
        t.z(), b.z(), normal.z();

    float u = payload.tex_coords.x();
    float v = payload.tex_coords.y();
    float w = payload.texture->width;
    float h = payload.texture->height;

    float dU = kh * kn * (payload.texture->getColor(u + 1 / w, v).norm() - payload.texture->getColor(u, v).norm());
    float dV = kh * kn * (payload.texture->getColor(u, v + 1 / h).norm() - payload.texture->getColor(u, v).norm());
    Vector3f ln(-dU, -dV, 1);
    point += (kn * normal * payload.texture->getColor(u, v).norm());

    normal = TBN*ln;

    Eigen::Vector3f result_color = {0, 0, 0};

    for (auto& light : lights)
    {
        // TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular* 
        // components are. Then, accumulate that result on the *result_color* object.
        Eigen::Vector3f light_direc = (light.position - point).normalized();
        Eigen::Vector3f vision_direc = (eye_pos - point).normalized();
        float r_pow2 = (light.position - point).dot(light.position - point);

        // diffuse
        Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r_pow2) * MAX(0, normal.normalized().dot(light_direc));
        // specular
        Eigen::Vector3f h = (vision_direc + light_direc).normalized();
        Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_pow2) * pow(MAX(0, normal.normalized().dot(h)), p);
        // ambient
        Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);

        result_color += (Ld + Ls + La);
    }
    return result_color * 255.f;
}

效果

1.normal map

 2.blinn-phong

3. 纹理贴图

 4.

 5.

 (本小节完)

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值