OpenGL纹理投影

在opengl-4-shading-language-cookbook-3rd这本书上有一章节 "Applying a projected texture"

这个例子本身是有点问题的,整个投影纹理原理和过程这里不讲述(和相机投影一样)

关键点是如何求出我们自定义相机视锥体内的顶点对应的纹理坐标,注意视锥体内,这点很重要,那本书就没注意这点。

物体顶点经过我们自定义观察坐标系最终投影到裁减空间的坐标(也就是matProj * matView * vertex)都是在一个平面上的,接下来我们需要执行透视除法(在opengl里这步我们无需关心,opengl会帮我们处理这一步,但是我们自定义的投影过程需要我们执行这一步),之后所有在视锥体内的点坐标都会归到 [-1,1] 之间,这个在 [-1,1] 之间的坐标就是我们需要的纹理坐标。但是在

在书里面相关观察和投影矩阵是这样定义的:

    vec3 projPos = vec3(5.0f,5.0f,5.0f);
    vec3 projAt = vec3(-2.0f,-4.0f,0.0f);
    vec3 projUp = vec3(0.0f,1.0f,0.0f);
    mat4 projView = glm::lookAt(projPos, projAt, projUp);
    mat4 projProj = glm::perspective(glm::radians(30.0f), 1.0f, 0.2f, 1000.0f);
    mat4 bias = glm::translate(mat4(1.0f), vec3(0.5f));
    bias = glm::scale(bias, vec3(0.5f));

    prog.setUniform("ProjectorMatrix", bias * projProj * projView);

这个bias是干什么的呢?,注意上面已经说过经过透视投影坐标会归到 [-1,1]之间,但是我们的纹理坐标却是 [0, 1]之间的,所以先乘以0.5再加上0.5就到 [0,1] 之间了。

它的顶点着色器是这样写的:

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

out vec3 EyeNormal;       // Normal in eye coordinates
out vec4 EyePosition;     // Position in eye coordinates
out vec4 ProjTexCoord;

uniform mat4 ProjectorMatrix;

uniform mat4 ModelViewMatrix;
uniform mat4 ModelMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVP;

void main()
{
    vec4 pos4 = vec4(VertexPosition,1.0);

    EyeNormal = normalize(NormalMatrix * VertexNormal);
    EyePosition = ModelViewMatrix * pos4;
    ProjTexCoord = ProjectorMatrix * (ModelMatrix * pos4);
    gl_Position = MVP * pos4;
}

片段着色器是这样写的:

#version 430

in vec3 EyeNormal;       // Normal in eye coordinates
in vec4 EyePosition;     // Position in eye coordinates
in vec4 ProjTexCoord;

layout(binding=0) uniform sampler2D ProjectorTex;
layout( location = 0 ) out vec4 FragColor;


void main() {
    vec3 color = blinnPhong(EyePosition.xyz, normalize(EyeNormal));

    vec3 projTexColor = vec3(0.0);

	if( ProjTexCoord.z > 0)
		projTexColor = textureProj( ProjectorTex, ProjTexCoord ).rgb;

    FragColor = vec4(color + projTexColor * 0.5, 1);
}

这里只贴出纹理投影相关代码,Phong光照部分没贴,不是我们的重点,运行程序效果着这样的:

这不看起来挺好的吗?有什么不对呢?

注意在代码里关于纹理设置

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, flowerTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

注意纹理缠绕设置的是GL_CLAMP_TO_BORDER,现在我们将他设置为GL_REPEAT:

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, flowerTex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

运行看效果:

发现所有的地方都被贴上了纹理,为什么会这样呢?
注意观察片段着色器代码:

#version 430

in vec3 EyeNormal;       // Normal in eye coordinates
in vec4 EyePosition;     // Position in eye coordinates
in vec4 ProjTexCoord;

layout(binding=0) uniform sampler2D ProjectorTex;
layout( location = 0 ) out vec4 FragColor;


void main() {
    vec3 color = blinnPhong(EyePosition.xyz, normalize(EyeNormal));

    vec3 projTexColor = vec3(0.0);

	if( ProjTexCoord.z > 0)
	    projTexColor = textureProj( ProjectorTex, ProjTexCoord ).rgb;

    FragColor = vec4(color + projTexColor * 0.5, 1);
}

这句话:
    if( ProjTexCoord.z > 0)
        projTexColor = textureProj( ProjectorTex, ProjTexCoord ).rgb;

也就是说只要投影点在相机的前方都会对纹理进行取色,其实这个判断是不对的,注意到目前为止我们还没有进行透视除法,也就是说现在ProjTexCoord的坐标并不是 在[0, 1]之间,所以书里面使用textureProj函数而不是常规的texture来取色,textureProj函数会帮我们进行透视除法也就是除以ProjTexCoord.w,在textureProj之前坐标是不确定的,所有这种做法是没办法精确判断当前的的点应不应该贴纹理,所以在我把纹理缠绕改成重复贴就会发现所有纹理坐标超出[0,1]的顶点也都被贴上纹理,因此我们不能使用textureProj函数,要自己执行透视除法和把坐标归到 [0,1] 之间,所有我们不需要在外面定义bias这个矩阵,矩阵相关代码修改如下:


    vec3 projPos = vec3(5.0f,5.0f,5.0f);
    vec3 projAt = vec3(-2.0f,-4.0f,0.0f);
    vec3 projUp = vec3(0.0f,1.0f,0.0f);
    mat4 projView = glm::lookAt(projPos, projAt, projUp);
    mat4 projProj = glm::perspective(glm::radians(30.0f), 1.0f, 0.2f, 1000.0f);

    prog.setUniform("ProjectorMatrix", projProj * projView);

顶点着色器不需要修改,片段着色器修改为:

#version 430

in vec3 EyeNormal;       // Normal in eye coordinates
in vec4 EyePosition;     // Position in eye coordinates
in vec4 ProjTexCoord;

layout(binding=0) uniform sampler2D ProjectorTex;
layout( location = 0 ) out vec4 FragColor;

void main() {
    vec3 color = blinnPhong(EyePosition.xyz, normalize(EyeNormal));

    vec3 projTexColor = vec3(0.0);

    vec2 coord = (ProjTexCoord.xy/ProjTexCoord.w + 1.0 ) /2.0;
    if( coord.x >= 0.0 && coord.x <= 1.0f && coord.y >= 0.0 && coord.y <= 1.0f)
       projTexColor = texture( ProjectorTex, coord ).rgb;

    FragColor = vec4(color + projTexColor * 0.5, 1);
}

这里我们自己执行透视除法并把坐标归到 [0,1 ],然后判断坐标是否在[0,1]之间,只有在这个范围内,也就是相机视锥体范围内,纹理才会被投影,效果如下:

这样即使纹理缠绕改成重复贴也不会有问题 ,

最后如果你想深究投影矩阵到底干了啥,可以看看这个文章:http://www.songho.ca/opengl/gl_projectionmatrix.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值