如何使用立方体贴图对未展开的模型进行纹理处理

Posted

技术标签:

【中文标题】如何使用立方体贴图对未展开的模型进行纹理处理【英文标题】:How to texture non-unwrapped model using a cubemap 【发布时间】:2019-04-29 05:01:38 【问题描述】:

我有很多没有展开的模型(它们没有 UV 坐标)。展开它们非常复杂。因此,我决定使用无缝立方体贴图对它们进行纹理处理:

[VERT]

attribute vec4 a_position;

varying vec3 texCoord;

uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
...

void main()

   gl_Position = u_projTrans * u_worldTrans * a_position;
   texCoord = vec3(a_position);
 


[FRAG]
varying vec3 texCoord;
uniform samplerCube u_cubemapTex;

void main()

  gl_FragColor = textureCube(u_cubemapTex, texCoord);

它可以工作,但结果很奇怪,因为纹理取决于顶点位置。如果我的模型比立方体或球体更复杂,我会在对象的某些部分看到可见的接缝和低分辨率的纹理。

反射在模型上映射得很好,但它具有镜像效果。

反思:

[VERT]
attribute vec3 a_normal;

varying vec3 v_reflection;

uniform mat4 u_matViewInverseTranspose;
uniform vec3 u_cameraPos;
...

void main()

   mat3 normalMatrix = mat3(u_matViewInverseTranspose);
   vec3 n = normalize(normalMatrix * a_normal);

   //calculate reflection
   vec3 vView = a_position.xyz - u_cameraPos.xyz;
   v_reflection = reflect(vView, n);

   ...

如何实现类似反射的东西,但具有“粘性”效果,这意味着好像纹理附加到某个顶点(不移动)。模型的每一面都必须显示立方体贴图自己的一面,因此它应该看起来像一个常见的 2D 纹理。任何建议将不胜感激。

更新 1

我总结了所有 cmets 并决定计算立方体贴图 UV。由于我使用的是 LibGDX,所以有些名称可能与 OpenGL 不同。

着色器类:

public class CubemapUVShader implements com.badlogic.gdx.graphics.g3d.Shader 
  ShaderProgram program;
  Camera camera;
  RenderContext context;

  Matrix4 viewInvTraMatrix, viewInv;

  Texture texture;
  Cubemap cubemapTex;

  ...

  @Override
  public void begin(Camera camera, RenderContext context) 
    this.camera = camera;
    this.context = context;
    program.begin();

    program.setUniformMatrix("u_matProj", camera.projection);
    program.setUniformMatrix("u_matView", camera.view);

    cubemapTex.bind(1);
    program.setUniformi("u_textureCubemap", 1);

    texture.bind(0);
    program.setUniformi("u_texture", 0);

    context.setDepthTest(GL20.GL_LEQUAL);       
    context.setCullFace(GL20.GL_BACK);
  

  @Override
  public void render(Renderable renderable) 
    program.setUniformMatrix("u_matModel", renderable.worldTransform);
    viewInvTraMatrix.set(camera.view);
    viewInvTraMatrix.mul(renderable.worldTransform);
    program.setUniformMatrix("u_matModelView", viewInvTraMatrix);
    viewInvTraMatrix.inv();
    viewInvTraMatrix.tra();
    program.setUniformMatrix("u_matViewInverseTranspose", viewInvTraMatrix);

    renderable.meshPart.render(program);
       
...

顶点:

attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec3 a_normal;
attribute vec3 a_tangent;
attribute vec3 a_binormal;

varying vec2 v_texCoord;
varying vec3 v_cubeMapUV;

uniform mat4 u_matProj;
uniform mat4 u_matView;
uniform mat4 u_matModel;

uniform mat4 u_matViewInverseTranspose;
uniform mat4 u_matModelView;


void main()
   
    gl_Position = u_matProj * u_matView * u_matModel * a_position;
    v_texCoord = a_texCoord0;       

    //CALCULATE CUBEMAP UV (WRONG!)
    //I decided that tm_l2g mentioned in comments is u_matView * u_matModel
    v_cubeMapUV = vec3(u_matView * u_matModel * vec4(a_normal, 0.0));

    /*
    mat3 normalMatrix = mat3(u_matViewInverseTranspose);

    vec3 t = normalize(normalMatrix * a_tangent);
    vec3 b = normalize(normalMatrix * a_binormal);
    vec3 n = normalize(normalMatrix * a_normal);    
    */

片段:

varying vec2 v_texCoord;
varying vec3 v_cubeMapUV;

uniform sampler2D u_texture;
uniform samplerCube u_textureCubemap;

void main()
    
  vec3 cubeMapUV = normalize(v_cubeMapUV);    
  vec4 diffuse = textureCube(u_textureCubemap, cubeMapUV);

  gl_FragColor.rgb = diffuse;

结果完全错误:

我期待这样的事情:

更新 2

如果我在顶点着色器中使用顶点位置作为立方体贴图坐标,则纹理看起来在侧面被拉伸并在某些地方扭曲:

v_cubeMapUV = a_position.xyz;

我上传了euro.blend、euro.obj 和cubemap 文件以供审核。

【问题讨论】:

【参考方案1】:

该代码仅适用于以(0,0,0) 为中心的网格,如果不是这种情况,或者即使(0,0,0) 不在网格内,也会出现伪影...

我将从计算网格的 BBOX BBOXmin(x0,y0,z0),BBOXmax(x1,y1,z1) 开始,并转换用于纹理坐标的位置,使其以它为中心:

center = 0.5*(BBOXmin+BBOXmax);
texCoord = vec3(a_position-center);

但是,不均匀的顶点密度仍然会导致纹理缩放伪影,尤其是在 BBOX 边尺寸差异太大的情况下。将其重新缩放为立方体会有所帮助:

vec3 center = 0.5*(BBOXmin+BBOXmax);  // center of BBOX
vec3 size   =      BBOXmax-BBOXmin;   // size of BBOX
vec3 r      =      a_position-center; // position centered around center of BBOX
r.x/=size.x; // rescale it to cube BBOX
r.y/=size.y;
r.z/=size.z;
texCoord = r;

同样,如果 BBOX 的中心不在网格内,那么这将不起作用...

反射部分我不清楚你有一些图像/截图吗?

[Edit1] 简单示例

我是这样看的(没有上面提到的中心偏移和纵横比校正):

[顶点]

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_g2s;
layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;

out smooth vec4 pixel_col;
out smooth vec3 pixel_txr;
//------------------------------------------------------------------
void main(void)
    
    pixel_col=col;
    pixel_txr=(tm_l2g*vec4(pos,0.0)).xyz;
    gl_Position=tm_g2s*tm_l2g*vec4(pos,1.0);
    
//------------------------------------------------------------------

[片段]

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
in smooth vec4 pixel_col;
in smooth vec3 pixel_txr;

uniform samplerCube txr_skybox;

out layout(location=0) vec4 frag_col;

//------------------------------------------------------------------
void main(void)
    
    frag_col=texture(txr_skybox,pixel_txr);
    
//------------------------------------------------------------------

在这里预览:

前几帧的白色圆环使用固定函数,其余使用着色器。如您所见,我使用的唯一输入是顶点position,color 和变换矩阵tm_l2g,它从网格坐标转换为全局世界,tm_g2s 保存透视投影......

如您所见,我使用与渲染模型相同的 CUBE MAP 纹理渲染 BBOX,因此它看起来像很酷的反射/透明效果 :)(这不是故意的)。

反正当我换行时

pixel_txr=(tm_l2g*vec4(pos,0.0)).xyz;

进入:

pixel_txr=pos;

在我的顶点着色器中,对象将再次成为实体:

您可以通过传递两个纹理坐标向量并在片段中获取两个纹素以某种比例将它们相加,从而将两者结合起来。粗略的你需要传递 2 个立方体贴图纹理,一个用于对象,一个用于天空盒 ...

红色警告来自我的 CPU 端代码,提醒我我正在尝试设置着色器中不存在的制服(正如我在 bump mapping example 中所做的那样,没有更改 CPU 端代码...)

[Edit1] 在此处预览带有偏移的网格

Vertex 发生了一些变化(只是添加了答案中描述的偏移):

//------------------------------------------------------------------
#version 420 core
//------------------------------------------------------------------
uniform mat4x4 tm_l2g;
uniform mat4x4 tm_g2s;
uniform vec3 center=vec3(0.0,0.0,2.0);

layout(location=0) in vec3 pos;
layout(location=1) in vec4 col;

out smooth vec4 pixel_col;
out smooth vec3 pixel_txr;
//------------------------------------------------------------------
void main(void)
    
    pixel_col=col;
    pixel_txr=pos-center;
    gl_Position=tm_g2s*tm_l2g*vec4(pos,1.0);
    
//------------------------------------------------------------------

因此,通过偏移中心点,您可以消除奇异点失真,但是正如我在 cmets 中提到的,对于任意网格,总会有一些失真,使用廉价的纹理技巧而不是正确的纹理坐标。

请注意我的网格已调整大小/标准化(遗憾的是,我不记得它的 <-1,+1> 范围或不同的 ona 并且懒得在我测试过的 GLSL 引擎的源代码中挖掘)所以偏移量可能有不同的大小在您的环境中实现相同的结果。

【讨论】:

我的网格居中 (0,0,0)。我从不移动它,只是围绕它旋转一个相机。是否可以不使用顶点位置(a_position)制作纹理坐标?您可以在 Internet 上随处找到反射部分的实现。例如,cubemaps @Nolesh 我知道反射是什么,但你想到的是什么?我假设Environmental CUBE map based reflection(但只是在这里猜测)?这与纹理坐标或顶点位置无关。它只使用面部法线......除非它“真实”并且基于相机位置...... 通过反射,我的意思是它不使用顶点位置并且在我的情况下可能有用。由于伪影,我不能依赖顶点位置。有没有办法只使用法线和相机视图(这些由反射使用)或其他东西来纹理对象侧面(不使用顶点位置)。 @Nolesh 是的,我最后一条评论中的链接 QA 使用的正是假设天空盒是一个 CUBE 地图并且它的“无限”大,所以对象/面部的相对位置无关紧要......你只是把脸正常并将其转换为相机局部空间(注意它的矢量不是位置,所以没有位移,所以w=0.0)并将其用作 CUBE 贴图坐标...获取纹素并将其添加到表面颜色... 听起来不错。您能否用代码扩展您的答案

以上是关于如何使用立方体贴图对未展开的模型进行纹理处理的主要内容,如果未能解决你的问题,请参考以下文章

使用立方体贴图(不是反射贴图)对球体进行纹理处理

CUDA 立方体贴图纹理

Opengl:如何对使用顶点数组制作的模型进行纹理处理?

DX12 纹理贴图

用OpenGL进行立方体表面纹理贴图

尝试对立方体贴图纹理进行采样时出现 GL_INVALID_OPERATION