从0开始做一个软渲染器 —— 法线变换

从0开始做一个软渲染器 —— 法线变换

1. 为什么要进行法线变换

如果只对模型的位置进行变换,例如旋转和缩放(位移不会改变法线),而不考虑法线进行相应变换,那么模型的光照就一整个乱掉。

2. 怎么进行法线变换

如果模型只是旋转或者XYZ等比例的缩放,那么法线直接乘以ModelMatrix不会有什么问题,但是如果不是等比例的缩放就会出现问题,参考文章图形学 | Shader |用一篇文章理解法线变换、切线空间、法线贴图 - 知乎 (zhihu.com)

假设光线是垂直向-Z方向,光照强度 = normallight.dir正常光照效果如图:

normal_corr.png

如果缩放比例XYZ=1,1,5,那么按理来说脸上法线正确情况会向两侧扩,也就是的光照强度变弱,脸会变暗。但是直接把法线乘以ModelMatrix的效果如下:

normal_err.png

脸明显变白了,显然法线出现错误。

如何进行正确的法线变换参考下面文章:

正确的normal transform matrix

(M1)T

求逆操作是一个消耗很大的过程,但是由于Scale Matrix, Rotate Matrix 和 Traslate Matrix的特殊性可以直接避免普通矩阵的求逆操作,参考下面两篇文章:

按照公式(不考虑T变换)

M=(X0Y0Z0T1),M1=(1|X|2X1|Y|2Y1|Z|2Z0T1|X|2XT1|Y|2YT1|Z|2Z1)

代码如下:

V2F BasicShader::VertexShader(const Vertex& v)
{
    V2F v2f;
    v2f.m_WorldPos	= v.m_Position * m_ModelMatrix;
    v2f.m_ScreenPos = v.m_Position * m_ModelMatrix * m_VPMatrix;

    //法线变换, 求变换矩阵
    float MagX_Square =	m_ModelMatrix.m_Mat[0][0] * m_ModelMatrix.m_Mat[0][0] +
                        m_ModelMatrix.m_Mat[0][1] * m_ModelMatrix.m_Mat[0][1] +
                        m_ModelMatrix.m_Mat[0][2] * m_ModelMatrix.m_Mat[0][2];

    float MagY_Square = m_ModelMatrix.m_Mat[1][0] * m_ModelMatrix.m_Mat[1][0] +
                        m_ModelMatrix.m_Mat[1][1] * m_ModelMatrix.m_Mat[1][1] +
                        m_ModelMatrix.m_Mat[1][2] * m_ModelMatrix.m_Mat[1][2];

    float MagZ_Square = m_ModelMatrix.m_Mat[2][0] * m_ModelMatrix.m_Mat[2][0] +
                        m_ModelMatrix.m_Mat[2][1] * m_ModelMatrix.m_Mat[2][1] +
                        m_ModelMatrix.m_Mat[2][2] * m_ModelMatrix.m_Mat[2][2];


    Matrix4 normalMatrix = m_ModelMatrix;

    normalMatrix.m_Mat[3][0] = 0;
    normalMatrix.m_Mat[3][1] = 0;
    normalMatrix.m_Mat[3][2] = 0;

    normalMatrix.m_Mat[0][0] /= MagX_Square;
    normalMatrix.m_Mat[0][1] /= MagX_Square;
    normalMatrix.m_Mat[0][2] /= MagX_Square;
    normalMatrix.m_Mat[1][0] /= MagY_Square;
    normalMatrix.m_Mat[1][1] /= MagY_Square;
    normalMatrix.m_Mat[1][2] /= MagY_Square;
    normalMatrix.m_Mat[2][0] /= MagZ_Square;
    normalMatrix.m_Mat[2][1] /= MagZ_Square;
    normalMatrix.m_Mat[2][2] /= MagZ_Square;


    v2f.m_Normal	= v.m_Normal * normalMatrix;
    v2f.m_Texcoord	= v.m_TexCoord;
    v2f.m_Color		= v.m_Color;


    return v2f;
}

结果如下图所示,符合预期:

normal_corr2.png

posted @   DogWealth~  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示