-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPhongWithShadow.hpp
193 lines (166 loc) · 5.41 KB
/
PhongWithShadow.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#pragma once
#include "Vertex.h"
#include "SceneContext.h"
#include "VertexShaderMatHelper.h"
#include "FrameBuffer.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <memory>
class PhongWithShadow
{
public:
using UseDerivative = std::false_type;
struct VSOut {
glm::vec4 proj_pos;
glm::vec4 normal;
glm::vec2 texcoord;
glm::vec3 world_pos;
glm::vec4 pos_from_light;
VSOut& operator+=(const VSOut& rhs)
{
proj_pos += rhs.proj_pos;
normal += rhs.normal;
texcoord += rhs.texcoord;
world_pos += rhs.world_pos;
pos_from_light += rhs.pos_from_light;
return *this;
}
VSOut operator+(const VSOut& rhs) const
{
return VSOut(*this) += rhs;
}
VSOut& operator*=(float v) {
proj_pos *= v;
normal *= v;
texcoord *= v;
world_pos *= v;
pos_from_light *= v;
return *this;
}
VSOut operator*(float rhs) const
{
return VSOut(*this) *= rhs;
}
void Lerp(const VSOut& v0, const VSOut& v1, const VSOut& v2, float a, float b, float c) noexcept {
normal = v0.normal * a + v1.normal * b + v2.normal * c;
texcoord = v0.texcoord * a + v1.texcoord * b + v2.texcoord * c;
world_pos = v0.world_pos * a + v1.world_pos * b + v2.world_pos * c;
pos_from_light = v0.pos_from_light * a + v1.pos_from_light * b + v2.pos_from_light * c;
}
};
class VertexShader : public VertexShaderMatHelper {
public:
glm::mat4 light_mvp;
VSOut operator()(const Vertex& v) const
{
glm::vec4 world_pos = model * glm::vec4(v.position, 1.0f);
return {
proj_view * world_pos,
obj_to_world_normal * glm::vec4(v.normal, 0.0f),
v.texcoord,
glm::vec3(world_pos),
light_mvp * glm::vec4(v.position, 1.0f)
};
}
};
class PixelShader {
public:
std::shared_ptr<SceneContext> pContext;
float qpow(float x, int n) {
float res = 1;
while (n) {
if (n & 1) {
res *= x;
}
x *= x;
n >>= 1;
}
return res;
}
glm::vec4 BlinnPhong(const VSOut& v, int modelId, int meshId) {
glm::vec3 light_pos = pContext->light->GetPosition();
glm::vec3 light_intensity = pContext->light->GetIntensity();
glm::vec3 camera_pos = pContext->camera_pos_cache;
glm::vec3 light_dir = pContext->light->GetDirection(v.world_pos);
float d = glm::length(light_pos - v.world_pos);
light_intensity /= d * d;
glm::vec3 N = glm::normalize(glm::vec3(v.normal));
glm::vec3 view_dir = glm::normalize(camera_pos - v.world_pos);
auto material_id = pContext->models[modelId]->meshes[meshId].material_idx;
auto& material = pContext->models[modelId]->materials[material_id];
glm::vec3 ka, kd, ks;
ka = material->Ka;
if (material->diffuse != nullptr)
kd = material->diffuse->Sample(v.texcoord.x, v.texcoord.y);
else
kd = material->Kd;
kd = glm::pow(kd, glm::vec3(2.2f));
if (material->specular != nullptr)
ks = material->specular->Sample(v.texcoord.x, v.texcoord.y);
else
ks = material->Ks;
glm::vec3 ambient = ka * glm::vec3(1.0f); //ka * ambient light intensity
dnl = glm::dot(N, light_dir);
glm::vec3 diffuse = kd * light_intensity * std::max(0.0f, dnl);
glm::vec3 half = glm::normalize(view_dir + light_dir);
glm::vec3 specular = ks * light_intensity * qpow(std::max(0.0f, glm::dot(N, half)), 150);
glm::vec3 color = ambient + diffuse + specular;
color = glm::pow(color, glm::vec3(1.0f / 2.2f));
color.r = std::max(0.0f, std::min(1.0f, color.r)); // Saturate
color.g = std::max(0.0f, std::min(1.0f, color.g));
color.b = std::max(0.0f, std::min(1.0f, color.b));
return glm::vec4(color, 1.0f);
}
float DecodeFloatFromRGBA(const glm::vec4& rgba)
{
return glm::dot(rgba, glm::vec4(1.0f, 1 / 255.0f, 1 / 65025.0f, 1 / 16581375.0f));
}
float dnl;
float CmpShadowMap(const glm::vec4& shadowCoord) {
if (shadowCoord.z > 1) return 1.0f;
float fZ = pContext->light->LookUpShadowMap(shadowCoord);
float bias = std::clamp(0.008f * tan(acos(dnl)), 0.005f, 0.01f);
if (fZ < shadowCoord.z - bias * shadowCoord.w) return 0.0f;
return 1.0f;
}
// for point light shadow
float CmpShadowMap2(const glm::vec3& world) {
glm::vec3 dir = world - pContext->light->GetPosition();
float current = glm::length(dir);
if (current > pContext->light->GetFarZ()) return 1.0f;
float fZ = pContext->light->LookUpShadowMap(glm::vec4(dir, 1.0f)) * pContext->light->GetFarZ();
float bias = std::clamp(2.0f * tan(acos(dnl)), 1.0f, 2.0f);
if (fZ < current - bias) return 0.0f;
return 1.0f;
}
float LinearDepth01(float Z) {
const float near = 0.1f;
const float far = 1000.0f;
const float div = far / near;
return 1.0f / ((1 - div) * Z + div);
}
glm::vec4 operator()(const VSOut& v, const VSOut& ddx, const VSOut& ddy, int modelId, int meshId)
{
glm::vec4 color = BlinnPhong(v, modelId, meshId);
#if 0 // for now, i don't want to create a new file.
// make sure of this when shading with shadow
// basic
glm::vec4 shadowCoord = v.pos_from_light / v.pos_from_light.w;
shadowCoord.x = shadowCoord.x * 0.5f + 0.5f;
shadowCoord.y = -shadowCoord.y * 0.5f + 0.5f;
shadowCoord.z = shadowCoord.z * 0.5f + 0.5f;
shadowCoord.w = 1.0f / v.pos_from_light.w;
float visibility = CmpShadowMap(shadowCoord);
#else
// omnidirectional shadow
float visibility = CmpShadowMap2(v.world_pos);
#endif
return visibility * color;
// depth visualize
//return glm::vec4(glm::vec3(LinearDepth01(v.proj_pos.z)), 1.0f);
}
};
public:
VertexShader vs;
PixelShader ps;
};