上一節我們自己寫好了一個數學庫,以供使用。
接下來,我們首先搭建如下架構:
ShaderBase:繪制shader。GeometryGenerator:幾何體結構。LightHelper:描述光照。BoxShader:我們demo的著色器實現。BoxDemo:我們最終展示的demo。
我們先實現以上部分功能。
1.ShderBase
我們只定義頂點著色器與像素/片元著色器(自己實現這兩個都差不多,下文統一稱呼為像素著色器)的基本實現。
ShaderBase.h
#pragma once
#include "Vertex.h"
#include "MVector.h"
#include "MMath.h"
#include "LightHelper.h"
class ShaderBase
{
public:
ShaderBase();
virtual ~ShaderBase();
public:
virtual VertexOut VS(const VertexIn& vin) = 0; //頂點著色器
virtual MVector PS(VertexOut& pin) = 0; //像素著色器
};
ShaderBase.cpp
#include "ShaderBase.h"
ShaderBase::ShaderBase()
{
}
ShaderBase::~ShaderBase()
{
}
2.1GeometryGenerator.h
我們定義幾何體結構,由頂點+索引組成,同時為該結構創建一個單例,并且創建一個立方體作為以后的demo。
#pragma once
#include "MMath.h"
class GeometryGenerator
{
private:
GeometryGenerator() {}
public:
//單例模式
static GeometryGenerator* GetInstance()
{
static GeometryGenerator instance;
return &instance;
}
//基本網絡結構:頂點集合+索引集合
struct MeshData
{
std::vector<VertexIn> vertices;
std::vector<UINT> indices;
};
//創建一個立方體:指定寬(X方向)、高(Y方向)、深(Z方向)
void CreateBox(float width, float height, float depth, MeshData &mesh);
};
2.1GeometryGenerator.cpp
填入各個部分的頂點和索引信息。
#include "./GeometryGenerator.h"
void GeometryGenerator::CreateBox(float width, float height, float depth, MeshData &mesh)
{
mesh.vertices.clear();
mesh.indices.clear();
//一共24個頂點(每面4個)
mesh.vertices.resize(24);
//一共36個索引(每面6個)
mesh.indices.resize(36);
float halfW = width * 0.5f;
float halfH = height * 0.5f;
float halfD = depth * 0.5f;
//眼睛面向z軸正方向
//構建頂點
//前面
mesh.vertices[0].pos = MVector(-halfW, -halfH, -halfD,1.f);
mesh.vertices[0].normal = MVector(0.f, 0.f, -1.f);
mesh.vertices[0].color = MVector(1.f, 0.f, 0.f,1.f);
mesh.vertices[0].tex = MFLOAT2(0.f, 1.f);
mesh.vertices[1].pos = MVector(-halfW, halfH, -halfD,1.f);
mesh.vertices[1].normal = MVector(0.f, 0.f, -1.f);
mesh.vertices[1].color = MVector(0.f, 0.f, 0.f, 1.f);
mesh.vertices[1].tex = MFLOAT2(0.f, 0.f);
mesh.vertices[2].pos = MVector(halfW, halfH, -halfD, 1.f);
mesh.vertices[2].normal = MVector(0.f, 0.f, -1.f);
mesh.vertices[2].color = MVector(1.f, 0.f, 0.f, 1.f);
mesh.vertices[2].tex = MFLOAT2(1.f, 0.f);
mesh.vertices[3].pos = MVector(halfW, -halfH, -halfD, 1.f);
mesh.vertices[3].normal = MVector(0.f, 0.f, -1.f);
mesh.vertices[3].color = MVector(0.f, 1.f, 0.f, 1.f);
mesh.vertices[3].tex = MFLOAT2(1.f, 1.f);
//省略其他面
......
}
3.1LightHelper.h:
描述光照,包含點光源,平行光,聚光燈以及材質。內容很簡單,自己看注釋吧。
注意
//入射光線關于法線的反射向量
MVector v = MathUtil::Reflect(-lightVec, normal);
float specFactor = pow(max(v.Dot(toEye), 0.0f), mat.specular.w);
//計算漫反射光
diffuse = mat.diffuse * L.diffuse * diffuseFactor;
//計算高光
spec = mat.specular * L.specular * specFactor;
#pragma once
#include <windows.h>
#include <algorithm>
#include "../Math/MMath.h"
namespace Lights {
//平行光
struct DirectionalLight
{
DirectionalLight() { ZeroMemory(this, sizeof(this)); }
MVector ambient; //環境光
MVector diffuse; //漫反射光
MVector specular; //高光
MVector direction; //光照方向
};
//點光源
struct PointLight
{
PointLight() { ZeroMemory(this, sizeof(this)); }
MVector ambient;
MVector diffuse;
MVector specular;
MVector position;//光源位置
MVector att; //衰減系數
float range; //光照范圍
};
//聚光燈
struct SpotLight
{
SpotLight() { ZeroMemory(this, sizeof(this)); }
MVector ambient;
MVector diffuse;
MVector specular;
MVector position; //光照位置
MVector direction; //光照方向
MVector att; //衰減系數
float range; //光照范圍
float spot; //光照強度系數
};
//材質
struct Material
{
Material() { ZeroMemory(this, sizeof(this)); }
MVector ambient;
MVector diffuse;
MVector specular;//w表示高光強度
MVector reflect;
};
//計算平行光
inline void ComputeDirectionalLight(
const Material& mat, //材質
const DirectionalLight& L, //平行光
MVector normal, //頂點法線
MVector toEye, //頂點到眼睛的向量
MVector & ambient, //計算結果:環境光
MVector & diffuse, //計算結果:漫反射光
MVector & spec) //計算結果:高光
{
// 結果初始化為0
ambient = MVector( 0.0f, 0.0f, 0.0f, 0.0f );
diffuse = MVector(0.0f, 0.0f, 0.0f, 0.0f);
spec = MVector(0.0f, 0.0f, 0.0f, 0.0f);
// 光線方向
MVector lightVec = -L.direction;
// 環境光直接計算
ambient = mat.ambient * L.ambient;
// 計算漫反射系數
//光線、法線方向歸一化
lightVec.Normalize();
float diffuseFactor = lightVec.Dot(normal);
// 頂點背向光源不再計算
if (diffuseFactor > 0.0f)
{
//入射光線關于法線的反射向量
MVector v = MathUtil::Reflect(-lightVec, normal);
float specFactor = pow(max(v.Dot(toEye), 0.0f), mat.specular.w);
//計算漫反射光
diffuse = mat.diffuse * L.diffuse * diffuseFactor;
//計算高光
spec = mat.specular * L.specular * specFactor;
}
}
//計算點光源
inline void ComputePointLight(
const Material& mat, //材質
PointLight L, //點光源
MVector pos, //頂點位置
MVector normal, //頂點法線
MVector toEye, //頂點到眼睛的向量
MVector& ambient, //計算結果:環境光
MVector& diffuse, //計算結果:漫反射光
MVector& spec) //計算結果:高光
{
ambient = MVector(0.0f, 0.0f, 0.0f, 0.0f);
diffuse = MVector(0.0f, 0.0f, 0.0f, 0.0f);
spec = MVector(0.0f, 0.0f, 0.0f, 0.0f);
//光照方向:頂點到光源
MVector lightVec = L.position - pos;
//頂點到光源距離
float d = MathUtil::Length(lightVec);
//超過范圍不再計算
if (d > L.range)
return;
//歸一化光照方向
lightVec = lightVec * (1.f / d);
//計算環境光
ambient = mat.ambient * L.ambient;
//漫反射系數
float diffuseFactor = lightVec.Dot(normal);
if (diffuseFactor > 0.0f)
{
MVector v = MathUtil::Reflect(-lightVec, normal);
float specFactor = pow(max(v.Dot(toEye), 0.0f), mat.specular.w);
//計算漫反射光
diffuse = mat.diffuse * L.diffuse * diffuseFactor;
//計算高光
spec = mat.specular * L.specular * specFactor;
}
// 計算衰減
float att = 1.f / L.att.Dot(MVector(1.f, d, d*d));
diffuse = diffuse * att;
spec = diffuse * att;
}
//計算聚光燈
inline void ComputeSpotLight(
const Material& mat, //材質
const SpotLight& L, //聚光燈
MVector pos, //頂點位置
MVector normal, //頂點法線
MVector toEye, //頂點到眼睛向量
MVector& ambient, //計算結果:環境光
MVector& diffuse, //計算結果:漫反射光
MVector& spec) //計算結果:高光
{
//初始化結果
ambient = MVector(0.0f, 0.0f, 0.0f, 0.0f);
diffuse = MVector(0.0f, 0.0f, 0.0f, 0.0f);
spec = MVector(0.0f, 0.0f, 0.0f, 0.0f);
//光照方向:頂點到光源
MVector lightVec = L.position - pos;
//頂點到光源距離
float d = MathUtil::Length(lightVec);
//距離大于光照方向不再計算
if (d > L.range)
return;
//歸一化光照方向
lightVec = lightVec * (1.f / d);
//計算環境光
ambient = mat.ambient * L.ambient;
//計算漫反射系數
float diffuseFactor = lightVec.Dot(normal);
if (diffuseFactor > 0.0f)
{
MVector v = MathUtil::Reflect(-lightVec, normal);
float specFactor = pow(max(v.Dot(toEye), 0.0f), mat.specular.w);
//漫反射光
diffuse = mat.diffuse * L.diffuse * diffuseFactor;
//高光
spec = mat.specular * L.specular * specFactor;
}
//聚光衰減系數
float spot = pow(max(-lightVec.Dot(L.direction), 0.0f), L.spot);
//衰減系數
float att = spot / L.att.Dot(MVector(1.0f, d, d*d));
ambient = ambient * spot;
diffuse = diffuse * att;
spec = spec * att;
}
}
4.1BoxShader.h:
定義下我們demo的著色器實現。
把我們的box實現model->world->view->clip->screen。
#pragma once
#include "ShaderBase.h"
#include "MMath.h"
class BoxShader : public ShaderBase
{
public:
BoxShader();
~BoxShader();
private:
MMatrix m_worldViewProj; //世界視角投影矩陣
Texture2D m_tex; //紋理
MMatrix m_world; //世界矩陣
MMatrix m_worldInvTranspose; //世界矩陣逆矩陣的轉置,用于調整法線
Lights::Material m_material; //材質
Lights::DirectionalLight m_dirLight; //平行光
MVector m_eyePos; //觀察點
public:
void SetWorldViewProj(const MMatrix& worldViewProj);
void SetTexture(const Texture2D& tex);
void SetWorld(const MMatrix& world);
void SetWorldInvTranspose(const MMatrix& worldInvTrans);
void SetEyePos(const MVector& eyePos);
void SetMaterial(const Lights::Material& material);
void SetDirLight(const Lights::DirectionalLight& dirLight);
public:
VertexOut VS(const VertexIn& vin); //頂點著色器
MVector PS(VertexOut& pin);
};
4.2BoxShader.cpp
重點只在于
//紋理+光照計算公式: 紋理*(環境光+漫反射光)+高光
MVector litColor = texColor * (ambient + diffuse) + specular;
//最終顏色透明度使用漫反射光的透明度
litColor.w = m_material.diffuse.w * texColor.w;
#include "BoxShader.h"
BoxShader::BoxShader()
{
}
BoxShader::~BoxShader()
{
}
void BoxShader::SetWorldViewProj(const MMatrix& worldViewProj)
{
m_worldViewProj = worldViewProj;
}
void BoxShader::SetTexture(const Texture2D& tex)
{
m_tex = tex;
}
void BoxShader::SetWorld(const MMatrix& world)
{
m_world = world;
}
void BoxShader::SetWorldInvTranspose(const MMatrix& worldInvTrans)
{
m_worldInvTranspose = worldInvTrans;
}
void BoxShader::SetEyePos(const MVector& eyePos)
{
m_eyePos = eyePos;
}
void BoxShader::SetMaterial(const Lights::Material& material)
{
m_material.ambient = material.ambient;
m_material.diffuse = material.diffuse;
m_material.reflect = material.reflect;
m_material.specular = material.specular;
}
void BoxShader::SetDirLight(const Lights::DirectionalLight& dirLight)
{
m_dirLight.ambient = dirLight.ambient;
m_dirLight.diffuse = dirLight.diffuse;
m_dirLight.direction = dirLight.direction;
m_dirLight.specular = dirLight.specular;
}
VertexOut BoxShader::VS(const VertexIn& vin)
{
VertexOut out;
out.posH = vin.pos * m_worldViewProj;
out.posTrans = vin.pos * m_world;
out.normal = out.normal * m_worldInvTranspose;
out.color = vin.color;
out.tex = vin.tex;
return out;
}
MVector BoxShader::PS(VertexOut& pin)
{
//單位化法線
pin.normal.Normalize();
//紋理采樣
MVector texColor = m_tex.Sample(pin.tex);
//頂點到觀察點向量
MVector toEye = (m_eyePos - pin.posTrans).Normalize();
//初始化顏色值全部為0
MVector ambient(0.f, 0.f, 0.f, 0.f);
MVector diffuse(0.f, 0.f, 0.f, 0.f);
MVector specular(0.f, 0.f, 0.f, 0.f);
//光源計算后得到的環境光、漫反射 、高光
MVector A, D, S;
Lights::ComputeDirectionalLight(m_material, m_dirLight, pin.normal, toEye, A, D, S);
ambient = ambient + A;
diffuse = diffuse + D;
specular = specular + S;
//紋理+光照計算公式: 紋理*(環境光+漫反射光)+高光
MVector litColor = texColor * (ambient + diffuse) + specular;
//最終顏色透明度使用漫反射光的透明度
litColor.w = m_material.diffuse.w * texColor.w;
return litColor;
}
5.1BoxDemo.h
我們定義出自己demo各部分所需要的數據結構,包括各種矩陣的運算,頂點、材質、紋理、光照等。
#pragma once
#include "Moe3D.h"
#include "BoxShader.h"
#include "MMath.h"
#include "GeometryGenerator.h"
class BoxDemo
{
public:
BoxDemo();
~BoxDemo();
public:
bool Init(HINSTANCE hInstance, HWND hWnd);
void Update(float dt);
void Render();
void Clear();
//鼠標事件控制
void OnMouseDown(WPARAM btnState, int x, int y);
void OnMouseUp(WPARAM btnState, int x, int y);
void OnMouseMove(WPARAM btnState, int x, int y);
public:
inline Moe3DDevice*& GetDevice() { return m_pDevice; }
private:
int m_width, m_height;
HINSTANCE m_hInstance;
HWND m_hWnd;
Moe3DDevice* m_pDevice;
Moe3DDeviceContext* m_pImmediateContext;
BoxShader* m_pShader;
MMatrix m_worldViewProj; //世界視角投影矩陣
MMatrix m_world; //世界變換矩陣
MMatrix m_worldInvTranspose; //世界變換逆矩陣的轉置 用于調整法線
std::vector<VertexIn> m_vertices; //頂點緩沖
std::vector<UINT> m_indices; //索引緩沖
GeometryGenerator::MeshData m_box;
Texture2D m_tex; //紋理
Lights::Material m_material; //材質
Lights::DirectionalLight m_dirLight; //平行光源
//控制攝像機位置角度等
float m_theta;
float m_phi;
float m_radius;
POINT m_lastMousePos;
};
經過以上的過程,我們自己搭建了一套數學庫,然后自己實現了圖片的讀取,這次我們又完善了光照的參數,以及模型空間-世界空間-視角空間-裁剪空間-屏幕空間的轉換。并且搭建了我們的Billiard框架,即一個正方體的各部分的信息結構,如頂點+索引+貼圖+材質等。
這節就到這,下次再把整個案例完成。
- vs2017宇宙最偉大IDE用Console等調試匯總
- c++Win32起始鼠標作圖181101
- 用迭代法找(兩圓的)交點-精確計算迭代并改進-數值周期1810
- 精度-比例關系181110P2點
- 用迭代法求找兩圓交點-精度計算181111A
- 月亮型-大小圓-上下圓算法181121
- 用c++的數學計算及圖形繪制總結之1/共4-181101
- 用c++做數學計算及圖形繪制總結之2/4-181102
- 用c++做數學計算及圖形繪制總結之3/4-181103
- 用c++做數學計算及圖形繪制總結4/4-181104
- 用c++的移位代替乘除運算181105
- 重構billiard2圓相交-非遞歸181101-非預料內圖形-原因分析181201
- 重構月亮型billiard202圓相交-非遞歸181102-非預料內圖形-原因分析181202
- 重構月亮型billiard202圓相交-非遞歸181102b-非預料內圖形-原因分析181202b
- 單橢圓(非遞歸)18圣誕后-ok版181225