编辑:您可能想从“编辑3”开始,因为我已经解决了很多问题
这是应用于普通层的普通立方体贴图的屏幕截图:
我的立方映射的icosphere的切线是使用以下代码生成的。索引的m_indices
中的std::vector
到std::vector
中的顶点的m_vertices
中。
std::vector<glm::vec3> storedTan(m_vertices.size(),glm::vec3(0,0,0));
// tangents
for(int i = 0; i < m_indices.size(); i+=3)
{
int i1 = m_indices[i];
int i2 = m_indices[i+1];
int i3 = m_indices[i+2];
VertexData v1 = m_vertices[i1];
VertexData v2 = m_vertices[i2];
VertexData v3 = m_vertices[i3];
glm::vec3 p1 = glm::vec3(v1.position[0],v1.position[1],v1.position[2]);
glm::vec3 p2 = glm::vec3(v2.position[0],v2.position[1],v2.position[2]);
glm::vec3 p3 = glm::vec3(v3.position[0],v3.position[1],v3.position[2]);
glm::vec3 t1 = glm::vec3(v1.tcoords[0],v1.tcoords[1],v1.tcoords[2]);
glm::vec3 t2 = glm::vec3(v2.tcoords[0],v2.tcoords[1],v2.tcoords[2]);
glm::vec3 t3 = glm::vec3(v3.tcoords[0],v3.tcoords[1],v3.tcoords[2]);
std::function<glm::vec2(glm::vec3)> get_uv = [=](glm::vec3 STR)
{
float sc, tc, ma;
float x = std::abs(STR.x);
float y = std::abs(STR.y);
float z = std::abs(STR.z);
if(x > y && x > z)
{
if(STR.x > 0)
{
sc = -STR.z;
tc = -STR.y;
ma = STR.x;
}
else
{
sc = STR.z;
tc = -STR.t;
ma = STR.x;
}
}
else if(y > z)
{
if(STR.y > 0)
{
sc = STR.x;
tc = STR.z;
ma = STR.y;
}
else
{
sc = STR.x;
tc = -STR.z;
ma = STR.y;
}
}
else
{
if(STR.z > 0)
{
sc = STR.x;
tc = -STR.y;
ma = STR.z;
}
else
{
sc = -STR.x;
tc = -STR.y;
ma = STR.z;
}
}
return glm::vec2((sc/std::abs(ma) + 1.0) / 2.0,(tc/std::abs(ma) + 1.0) / 2.0);
};
glm::vec2 uv1 = get_uv(t1);
glm::vec2 uv2 = get_uv(t2);
glm::vec2 uv3 = get_uv(t3);
glm::vec3 edge1 = p2 - p1;
glm::vec3 edge2 = p3 - p1;
glm::vec2 tedge1 = uv2 - uv1;
glm::vec2 tedge2 = uv3 - uv1;
float r = 1.0f / (tedge1.x * tedge2.y - tedge2.x - tedge1.y);
glm::vec3 sdir((tedge2.y * edge1.x - tedge1.y * edge2.x) * r,
(tedge2.y * edge1.y - tedge1.y * edge2.y) * r,
(tedge2.y * edge1.z - tedge1.y * edge2.z) * r);
glm::vec3 tdir((tedge1.x * edge2.x - tedge2.x * edge1.x) * r,
(tedge1.x * edge2.y - tedge2.x * edge1.y) * r,
(tedge1.x * edge2.z - tedge2.x * edge1.z) * r);
m_vertices[i1].tangent[0] += sdir.x;
m_vertices[i1].tangent[1] += sdir.y;
m_vertices[i1].tangent[2] += sdir.z;
m_vertices[i2].tangent[0] += sdir.x;
m_vertices[i2].tangent[1] += sdir.y;
m_vertices[i2].tangent[2] += sdir.z;
m_vertices[i3].tangent[0] += sdir.x;
m_vertices[i3].tangent[1] += sdir.y;
m_vertices[i3].tangent[2] += sdir.z;
storedTan[i1] += sdir;
storedTan[i2] += sdir;
storedTan[i3] += sdir;
}
for(int i = 0; i < m_vertices.size(); ++i)
{
glm::vec3 n = glm::vec3(m_vertices[i].normal[0],m_vertices[i].normal[1],m_vertices[i].normal[2]);
glm::vec3 t = glm::vec3(m_vertices[i].tangent[0],m_vertices[i].tangent[1],m_vertices[i].tangent[2]);
glm::vec3 newT = glm::normalize(t - n * glm::dot(n,t));
m_vertices[i].tangent[0] = newT.x;
m_vertices[i].tangent[1] = newT.y;
m_vertices[i].tangent[2] = newT.z;
m_vertices[i].tangent[3] = (glm::dot(glm::cross(n,t), storedTan[i]) < 0.0f) ? -1.0f : 1.0f;
}
我的VertexData看起来像这样:
struct VertexData
{
GLfloat position[4];
GLfloat normal[3];
GLfloat tcoords[3];
GLfloat tangent[4];
};
我知道当前的
tcoords
,position
和normal
很好(否则,您将看不到上面的屏幕截图)。然后我的顶点着色器看起来像这样:
#version 400
layout (location = 0) in vec4 in_position;
layout (location = 1) in vec3 in_normal;
layout (location = 2) in vec3 in_UV;
layout (location = 3) in vec4 in_tangent;
struct PointLight
{
bool active;
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightMVP;
uniform PointLight uLight;
smooth out vec3 ex_UV;
out vec3 ex_normal;
out vec3 ex_positionCameraSpace;
out vec3 ex_originalPosition;
out vec3 ex_positionWorldSpace;
out vec4 ex_positionLightSpace;
out vec3 ex_tangent;
out vec3 ex_binormal;
out PointLight ex_light;
void main()
{
gl_Position = projection * view * model * in_position;
ex_UV = in_UV;
ex_normal = mat3(transpose(inverse(view * model))) * in_normal;
ex_positionCameraSpace = vec3(view * model * in_position);
ex_originalPosition = vec3(in_position.xyz);
ex_positionWorldSpace = vec3(model*in_position);
ex_positionLightSpace = lightMVP * model * in_position;
ex_tangent = mat3(transpose(inverse(view * model))) * in_tangent.xyz;
ex_binormal = cross(ex_normal,ex_tangent);
// provide the fragment shader with a light in view space rather than world space
PointLight p = uLight;
p.position = vec3(view * vec4(p.position,1.0));
ex_light = p;
}
最后,我的片段着色器如下所示:
#version 400
layout (location = 0) out vec4 color;
struct Material
{
bool useMaps;
samplerCube diffuse;
samplerCube specular;
samplerCube normal;
float shininess;
vec4 color1;
vec4 color2;
};
struct PointLight
{
bool active;
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
uniform Material uMaterial;
smooth in vec3 ex_UV;
in vec3 ex_normal;
in vec3 ex_positionCameraSpace;
in vec3 ex_originalPosition;
in vec3 ex_positionWorldSpace;
in vec4 ex_positionLightSpace;
in vec3 ex_tangent;
in vec3 ex_binormal;
in PointLight ex_light;
/* ******************
Provides a better lookup into a cubemap
******************* */
vec3 fix_cube_lookup(vec3 v, float cube_size)
{
float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
float scale = (cube_size - 1) / cube_size;
if (abs(v.x) != M)
v.x *= scale;
if (abs(v.y) != M)
v.y *= scale;
if (abs(v.z) != M)
v.z *= scale;
return v;
}
/* *********************
Calculates the color when using a point light. Uses shadow map
********************* */
vec3 CalcPointLight(PointLight light, Material mat, vec3 normal, vec3 fragPos, vec3 originalPos, vec3 viewDir)
{
// replace the normal with lookup normal. This is now in tangent space
vec3 textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.normal,0).x);
normal = texture(mat.normal,textureLookup).rgb;
// the direction the light is in in the light position - fragpos
// light dir and view dir are now in tangent space
vec3 lightDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * normalize(fragPos - light.position);
viewDir = transpose(mat3(ex_tangent,ex_binormal,ex_normal)) * viewDir;
// get the diffuse color
textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.diffuse,0).x);
vec3 diffuseMat = vec3(0.0);
if(mat.useMaps)
diffuseMat = texture(mat.diffuse,textureLookup).rgb;
else
diffuseMat = mat.color1.rgb;
// get the specular color
textureLookup = fix_cube_lookup(normalize(ex_originalPosition),textureSize(mat.specular,0).x);
vec3 specularMat = vec3(0.0);
if(mat.useMaps)
specularMat = texture(mat.specular,textureLookup).rgb;
else
specularMat = mat.color2.rgb;
// the ambient color is the amount of normal ambient light hitting the diffuse texture
vec3 ambientColor = light.ambient * diffuseMat;
// Diffuse shading
float diffuseFactor = dot(normal, -lightDir);
vec3 diffuseColor = vec3(0,0,0);
vec3 specularColor = vec3(0,0,0);
if(diffuseFactor > 0)
diffuseColor = light.diffuse * diffuseFactor * diffuseMat;
// Specular shading
vec3 reflectDir = normalize(reflect(lightDir, normal));
float specularFactor = pow(dot(viewDir,reflectDir), mat.shininess);
if(specularFactor > 0 && diffuseFactor > 0)
specularColor = light.specular * specularFactor * specularMat;
float lightDistance = length(fragPos - light.position);
float attenuation = light.constant + light.linear * lightDistance + light.quadratic * lightDistance * lightDistance;
return ambientColor + (diffuseColor + specularColor) / attenuation;
}
void main(void)
{
vec3 norm = normalize(ex_normal);
vec3 viewDir = normalize(-ex_positionCameraSpace);
vec3 result = CalcPointLight(ex_light,uMaterial,norm,ex_positionCameraSpace, ex_positionWorldSpace,viewDir);
color = vec4(result,1.0);
}
据我所知:
结果什么都没有。即屏幕上没有任何内容。根本不是纯色。因此,就像后面的所有内容都被绘制成无遮挡一样。
如果将查找丢弃到法线贴图中,而是仅使用切线矩阵光源并查看,则会得到以下结果:
此镜头上有一个后处理镜头光斑,可产生那些有趣的碎片和鲍勃。我认为重要的是,表面法线看上去有些准确,但表面产生了压倒性的眩光。
如果我然后仅通过切线矩阵对光进行变换,则会得到以下结果:
所有这些结合起来告诉我,我不知道我要去哪里错了。
我暗示这是我的切线一代,因为其他作品似乎都遵循我阅读过的每篇教程所讲的内容。切线是在考虑了立方映射的icosphere的情况下生成的。因此,要从多维数据集的常规3D坐标确定
<S,T>
或<U,V>
2D坐标,我:这是我正在谈论的https://www.opengl.org/registry/specs/ARB/texture_cube_map.txt的摘录。
major axis
direction target sc tc ma
---------- ------------------------------- --- --- ---
+rx TEXTURE_CUBE_MAP_POSITIVE_X_ARB -rz -ry rx
-rx TEXTURE_CUBE_MAP_NEGATIVE_X_ARB +rz -ry rx
+ry TEXTURE_CUBE_MAP_POSITIVE_Y_ARB +rx +rz ry
-ry TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB +rx -rz ry
+rz TEXTURE_CUBE_MAP_POSITIVE_Z_ARB +rx -ry rz
-rz TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB -rx -ry rz
Using the sc, tc, and ma determined by the major axis direction as
specified in the table above, an updated (s,t) is calculated as
follows
s = ( sc/|ma| + 1 ) / 2
t = ( tc/|ma| + 1 ) / 2
This new (s,t) is used to find a texture value in the determined
face's 2D texture image using the rules given in sections 3.8.5
and 3.8.6." ...
编辑
我不知道为什么不这样做,但是我已经在几何着色器中输出了法线,切线和切线,以查看它们的朝向。我用this tutorial。
Here they are
黄色是面法线,绿色是顶点法线。我不确定为什么顶点法线看起来不对,为什么它们不影响其他任何照明,所以这可能只是我的几何着色器中的一个错误。
切线为红色,法线为蓝色。这些似乎(很难说出)好像彼此之间是相互垂直的,这是正确的,但是除了它们没有指向统一的方向之外。这就是我以前斑驳的图案。
我不知道该如何解决。
编辑2
我已经解决了显示法线等问题。现在已解决。
结果,我添加了一些阴影使其更清晰,每种颜色都是不同的立方体面。
我更改的其他内容是对法线贴图的查找。我忘了将范围调整回-1到1(从0到1)。
normal = texture(mat.normal,textureLookup).rgb * 2.0 - 1.0;
这不能解决我的问题。
令人困惑的是,当我尝试使用纹理中的法线时,我什么都没渲染。没有任何东西进入深度缓冲区。我已经检查并再次检查纹理是否可以从着色器访问(因此,原始屏幕截图显示了应用于球体的纹理)。
因为即使我的切线和法线都指向各个方向;我仍然希望能够显示某些内容,即使它是错误的。但是,甚至没有环境颜色通过。 (即使我不留我的
lightDir
和viewdir
也会发生这种情况。如果我只是忽略顶点法线并查找纹理。我会丢失环境颜色)...编辑3:最后一个问题
通常情况下,问题的一部分与您认为错误的地方无关。我的问题是我用其他纹理覆盖了法线贴图的绑定(bind)。
因此,通过这种方式,我现在可以看到我的颜色通过了。用我漂亮的性感凹凸贴图。
但是,立方体贴图的接缝处现在存在问题。我不确定这是因为正切值的计算还是法线贴图的生成方式。我的法线贴图是根据每个面孔的高度贴图独立生成的。
这将解释我认为对接缝的影响,我将对其进行修改以对这些边缘上的相邻面进行采样,然后观察会发生什么情况。
我仍然认为生成的切线也将对这些接缝产生不利影响。我的想法是,它们将在接缝处指向相反的方向。
屏幕截图:
编辑4
从EDIT1向下测试时,我为icosphere使用了非常低的多边形网格。所以我的 segmentation 很少。
我想看看带有很多多边形的法线贴图球看起来不太完美。这立即揭示了这个问题:
如果不清楚,我的老 friend 缝是从左开始书写,但在下面是三角形的边缘。
因此,经过以上所有,我想我回到了切线不正确的原始问题。
仍在寻找任何正在阅读本文的人的帮助。
编辑4
好吧,那很快。此站点http://www.geeks3d.com/20130122/normal-mapping-without-precomputed-tangent-space-vectors/给了我另一种创建切线的方法。尽管代码看起来与我在CPU上执行的操作有些相似,但是它并没有导致那些随机定向的切线从EDIT 3中产生那些边缘。
我现在很近。我仍然有接缝,这另一种产生切线的方法似乎增加了它们的“seaminess”
编辑5
我现在尝试修改法线贴图的生成。先前的代码如下所示:
for(int i = 0; i < 6; ++i)
{
float scale = 15.0;
std::deque<glm::vec4> normalMap(textureSize*textureSize);
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
// center point
int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
float v11 = cubeFacesHeight[i][i11].r;
// to the left
int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
float v01 = cubeFacesHeight[i][i01].r;
// to the right
int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
float v21 = cubeFacesHeight[i][i21].r;
// to the top
int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
float v10 = cubeFacesHeight[i][i10].r;
// and now the bottom
int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
float v12 = cubeFacesHeight[i][i12].r;
glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);
glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));
N.x = (N.x+1.0)/2.0;
N.y = (N.y+1.0)/2.0;
N.z = (N.z+1.0)/2.0;
normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
}
}
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
}
}
}
cubeFacesHeight
是std::array
的6
的std::deque
。或者,我的立方体贴图的六个面。脸上的颜色是灰度的,出于无关紧要的原因,我没有使用浮标。我现在将其更改为以下内容,警告,这很丑陋而且很长。
for(int i = 0; i < 6; ++i)
{
// 0 is negative X
// 1 is positive X
// 2 is negative Y
// 3 is positive Y
// 4 is negative Z
// 5 is positive Z
// +X: right -Z (left), left +Z (right), top -Y (right), bottom +Y (right)
// -X: right +Z (left), left -Z (right), top -Y (left), bottom +Y (left)
// -Z: right -X (left), left +X (right), top -Y (bottom), bottom +Y (top)
// +Z: right +X (left), left -X (right), top -Y (top), bottom +Y (bottom)
// -Y: right +X (top), left -X (top), top +Z (top), bottom -Z (top)
// +Y: right +X (bottom), left -X (bottom), top -Z (bottom), bottom +Z (bottom)
//+Z is towards, -Z is distance
const int NEGATIVE_X = 0;
const int NEGATIVE_Y = 2;
const int NEGATIVE_Z = 4;
const int POSITIVE_X = 1;
const int POSITIVE_Y = 3;
const int POSITIVE_Z = 5;
float scale = 15.0;
std::deque<glm::vec4> normalMap(textureSize*textureSize);
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
// center point
int i11 = utils::math::get_1d_array_index_from_2d(x,y,textureSize);
float v11 = cubeFacesHeight[i][i11].r;
// to the left
int i01 = utils::math::get_1d_array_index_from_2d(std::max(x-1,0),y,textureSize);
float v01 = cubeFacesHeight[i][i01].r;
if(x-1 < 0)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
}
// to the right
int i21 = utils::math::get_1d_array_index_from_2d(std::min(x+1,textureSize-1),y,textureSize);
float v21 = cubeFacesHeight[i][i21].r;
if(x+1 > textureSize-1)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[NEGATIVE_X][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(0,y,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(y,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_X][i01].r;
}
}
// to the top
int i10 = utils::math::get_1d_array_index_from_2d(x,std::max(y-1,0),textureSize);
float v10 = cubeFacesHeight[i][i10].r;
if(y-1 < 0)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Y][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
}
// and now the bottom
int i12 = utils::math::get_1d_array_index_from_2d(x,std::min(y+1,textureSize-1),textureSize);
float v12 = cubeFacesHeight[i][i12].r;
if(y+1 > textureSize-1)
{
if(i == NEGATIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(0,x,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == POSITIVE_X)
{
i01 = utils::math::get_1d_array_index_from_2d(textureSize-1,x,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == NEGATIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == POSITIVE_Z)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_Y][i01].r;
}
else if(i == NEGATIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,0,textureSize);
v01 = cubeFacesHeight[NEGATIVE_Z][i01].r;
}
else if(i == POSITIVE_Y)
{
i01 = utils::math::get_1d_array_index_from_2d(x,textureSize-1,textureSize);
v01 = cubeFacesHeight[POSITIVE_Z][i01].r;
}
}
glm::vec3 S = glm::vec3(1, 0, scale * v21 - scale * v01);
glm::vec3 T = glm::vec3(0, 1, scale * v12 - scale * v10);
glm::vec3 N = (glm::vec3(-S.z,-T.z,1) / std::sqrt(S.z*S.z + T.z*T.z + 1));
N.x = (N.x+1.0)/2.0;
N.y = (N.y+1.0)/2.0;
N.z = (N.z+1.0)/2.0;
normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = glm::vec4(N.x,N.y,N.z,v11);
}
}
for(int x = 0; x < textureSize; ++x)
{
for(int y = 0; y < textureSize; ++y)
{
cubeFacesHeight[i][utils::math::get_1d_array_index_from_2d(x,y,textureSize)] = normalMap[utils::math::get_1d_array_index_from_2d(x,y,textureSize)];
}
}
}
因此,我现在有点“渗出”到相邻的立方体面中,以在生成法线贴图的同时对那里的高度进行采样。这实际上增加了接缝外观。
但这引发了自己的问题。例如...“为什么在 hell 中增加了影响?”您可以看到它现在是一种斜角效果。
因此,我相当确定我在“渗入”到下一个立方体时正确地匹配了我的立方体。这使我回到切线不正确的地方。
即使我完全混合了立方体的面,也不会产生斜角效果,那将是完全 Blob 的。例如,即使在完全平坦的截面上,即将法线贴图生成到下一个面中,其效果也为零,但我仍然看到巨大的斜角。
这使我认为,如果切线之前是正确的,法线贴图会“匹配”切线方向吗?我不知道。
快速编辑
我注意到在原始 map 生成过程中,我有效地对我的脸部边缘进行了两次采样。如果我删除了这个双重采样,而仅使用0作为附加采样,我最终会看到那些相同的接缝。我不确定这意味着什么...
另一个快速编辑
该图像显示了一些我认为非常有说服力的东西。
我在这里可以看到两个不同的面孔“指向”相反的方向。这与我的切线切线生成有关。
所以我回到正切问题了。
最佳答案
法线贴图与最初创建法线贴图的法线 vector 矩阵配合使用效果最佳
我相信您的问题与切线在整个曲面上的非均匀对齐有关。通常,UV贴图是处理此类问题的第一位。而且,用2D图像绘制球体并不是那么容易(查看所有各种地球投影拓扑,您会明白我的意思)。在某个时候,您将得到拉伸(stretch),边缘或剪切,并且很可能是上述所有的某种组合。通常使用UV贴图,重点是选择要在表面上隐藏这些效果的位置。为此经常选择行星的两极。
我希望看到的一个地方是重新排列您的切线和法线,以使它们都共享一个共同的全局方向,即。 tanget =北,binormal =东,法线朝外(海拔)。
切线和双法线的不均匀性在法线贴图问题中有时会出现的工件中起直接作用,因为如果法线图是在假设所有切线和双法线的方向一致。
本质上,法线贴图是在对切线和双法线具有隐式理解的情况下烘焙/创建的。如果在重新应用法线贴图时,曲面的切线和双法线与最初创建法线贴图的隐含理解不一致,那么您将得到光照和阴影错误。
效益或正交法 vector 矩阵
这样做的好处是,通常使用切线和双法线 vector 来查找2D纹理坐标。如果矩阵不是正交的,则存在在倾斜角度处发生剪切,旋转或精度损失的风险。
定义均匀的正交法线 vector 矩阵
您可以采用另一种谨慎的方式来处理法线/切线/双法线计算,这样可以确保两个因素:
这将通过两次旋转和一次移动来转换预定义的正交 vector 矩阵而起作用。为了说明起见,我不会将这三个矩阵运算折叠成一个矩阵,但是您可能应该在代码中这样做。
首先,从已经定义的 vector 矩阵开始
vec3 = [1, 0, 0, 0, 1, 0, 0, 0, 1];
其次,在对象空间而不是世界空间中执行这些操作
否则,您将不得不将该对象转换回世界中心,然后将其旋转回到其原点方向,然后应用法线转换,然后将对象发送回其工作位置和方向
第三,创建一个从vtx [n]到对象中心的 vector
此 vector 将告诉您将法线 vector 矩阵向两个方向旋转多少:
第四,旋转法线 vector 矩阵以对齐
最后,将法线 vector 矩阵移动一定距离
冲洗并重复
如果需要保持不均匀,非正交的UV
您可以基于不协调的UV布局创建法线贴图,这样它将使该布局生效,因此适当地自身应用而不会生效。但是必须根据这种先天的不一致性来创建法线贴图,以便将其优雅地应用于这些UV。
边缘像素插值?
第三,查看法线贴图折痕的边缘如何跟随立方体贴图的形状,我想知道您如何为法线贴图插入边缘像素。
用于立方体贴图的GLSL纹理查找?
另外,也许我只是没有找到答案的这一部分来解决这个问题,但是您是否考虑过使用GLSL cubemap查找功能?
gvec4 texture( gsamplerCube sampler, vec3 P, [float bias]);
关于c++ - 我的法线映射有什么问题?我认为这是我的切线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30437457/