0

有人可以帮助我或引导我朝着正确的方向在 GLSL 代码中实现 DirectX 的基本 FVF 吗?我完全了解如何创建程序、应用 VBO 以及所有这些,但我在实际创建着色器时遇到了很大的困难。即:

transformed+lit (x,y,color,specular,tu,tv)
lit (x,y,z,color,specular,tu,tv)
unlit (x,y,z,nx,ny,nz,tu,tv) [material/lights]

有了这个,我将有足够的时间来实现更有趣的着色器。

所以,我并不是要一种机制来处理 FVF。我只是在给定正确的流的情况下询问着色器代码。我知道 unlit 和 lit 版本依赖于传递矩阵,我完全理解这个概念。我只是很难找到显示这些概念的着色器示例。

4

2 回答 2

2

好的。如果您在寻找工作着色器时遇到问题,可以参考示例(老实说,您可以在任何 OpenGL 书籍中找到它)。

此着色器程序将使用您的对象的世界矩阵和相机的矩阵来变换顶点,然后将一个纹理映射到像素并用一个定向光照亮它们(根据材质属性和光方向)。

顶点着色器:

#version 330

// Vertex input layout
attribute vec3 inPosition;
attribute vec3 inNormal;
attribute vec4 inVertexCol;
attribute vec2 inTexcoord;
attribute vec3 inTangent;
attribute vec3 inBitangent;

// Output
struct PSIn
{
    vec3 normal;
    vec4 vertexColor;
    vec2 texcoord;
    vec3 tangent;
    vec3 bitangent;
};

out PSIn psin;

// Uniform buffers
layout(std140)
uniform CameraBuffer
{
    mat4 mtxView;
    mat4 mtxProj;
    vec3 cameraPosition;
};

layout(std140)
uniform ObjectBuffer
{
    mat4 mtxWorld;
};

void main()
{
    // transform position
    vec4 pos = vec4(inPosition, 1.0f);
    pos = mtxWorld * pos;
    pos = mtxView * pos;
    pos = mtxProj * pos;
    gl_Position = pos;

    // just pass-through other stuff
    psin.normal = inNormal;
    psin.tangent = inTangent;
    psin.bitangent = inBitangent;
    psin.texcoord = inTexcoord;
    psin.vertexColor = inVertexCol;
}

和片段着色器:

#version 330

// Input
in vec3 position;
in vec3 normal;
in vec4 vertexColor;
in vec2 texcoord;
in vec3 tangent;
in vec3 bitangent;

// Output
out vec4 fragColor;

// Uniforms
uniform sampler2D sampler0;

layout(std140)
uniform CameraBuffer
{
    mat4 mtxView;
    mat4 mtxProj;
    vec3 cameraPosition;
};

layout(std140) 
uniform ObjectBuffer
{
    mat4 mtxWorld;
};

layout(std140) 
uniform LightBuffer
{
    vec3 lightDirection;
};

struct Material
{
    float Ka;  // ambient quotient
    float Kd;  // diffuse quotient
    float Ks;  // specular quotient
    float A;   // shininess
};

layout(std140) 
uniform MaterialBuffer
{
    Material material;
};

    // function to calculate pixel lighting
float Lit( Material material, vec3 pos, vec3 nor, vec3 lit, vec3 eye )
{
    vec3 V = normalize( eye - pos );
    vec3 R = reflect( lit, nor);

    float Ia = material.Ka;
    float Id = material.Kd * clamp( dot(nor, -lit), 0.0f, 1.0f );
    float Is = material.Ks * pow( clamp(dot(R,V), 0.0f, 1.0f), material.A );
    return Ia + Id + Is;
}

void main() 
{   
    vec3 nnormal = normalize(normal);
    vec3 ntangent = normalize(tangent);
    vec3 nbitangent = normalize(bitangent);

    vec4 outColor = texture(sampler0, texcoord); // texture mapping

    outColor *=  Lit( material, position, nnormal, lightDirection, cameraPosition ); // lighting

    outColor.w = 1.0f;

    fragColor = outColor;
}

如果你不想要纹理,就不要采样纹理,而是等同outColorvertexColor.

如果您不需要照明,只需注释掉Lit()功能。

编辑: 对于 2D 对象,您仍然可以使用相同的程序,但许多功能将是多余的。你可以去掉:

  • 相机
  • 材料
  • 所有的 vertex attributes,但是inPositionand inTexcoord(也许inVertexCol你也需要 vertexs 有颜色)和所有与不需要attribute的 s相关的代码
  • inPositionvec2
  • 您将需要传递正交投影矩阵而不是透视一
  • 您甚至可以剥离矩阵,并以像素为单位传递顶点缓冲区。请参阅我的回答了解如何将这些像​​素位置转换为屏幕空间位置。您可以在 C/C++ 代码或 GLSL/HLSL 中执行此操作。

希望它以某种方式有所帮助。

于 2013-10-09T11:35:48.413 回答
1

介绍

你没有指定你的目标 OpenGL/GLSL 版本,所以我假设它至少是 OpenGL 3。

与固定功能管线相比,可编程管线的主要优势之一是完全可定制的顶点输入。我不太确定,如果引入固定顶点格式等约束是个好主意。为了什么?..(您会在我的帖子的“另一种方式”段落中找到现代方法)

但是,如果你真的想模拟固定功能......

  1. 我认为您需要为您拥有的每种顶点格式都有一个顶点着色器,或者以某种方式即时生成顶点着色器。甚至适用于所有着色器阶段。

    例如,对于x, y, color, tu, tv输入,您将拥有顶点着色器,例如:

    attribute vec2 inPosition;
    attribute vec4 inCol;
    attribute vec2 inTexcoord;
    
    void main()
    {
    ...
    }
    
  2. 由于您在 OpenGL 3 中没有固定的变换、光照和材质功能,因此您必须自己实现它:

    • 您必须传递矩阵进行转换
    • 对于光照着色器,您必须传递其他变量,例如光照方向
    • 对于材质着色器,您必须在输入中有材质

    通常,在着色器中,您使用制服或制服块:

    layout(std140)
    uniform CameraBuffer
    {
        mat4 mtxView;
        mat4 mtxProj;
        vec3 cameraPosition;
    };
    
    layout(std140) 
    uniform ObjectBuffer
    {
        mat4 mtxWorld;
    };
    
    layout(std140) 
    uniform LightBuffer
    {
        vec3 lightDirection;
    };
    
    struct Material
    {
        float Ka; 
        float Kd; 
        float Ks;
        float A;
    };
    
    layout(std140) 
    uniform MaterialBuffer
    {
        Material material;
    };
    
  3. 也许,您可以以某种方式将所有具有不同格式、制服等的着色器组合在一个带有分支的大型ubershader中。

另一种方式

您可以坚持现代方法,只允许用户声明他想要的顶点格式(格式,他在他的着色器中使用)。只需实现类似于IDirect3DDevice9::CreateVertexDeclarationID3D11Device::CreateInputLayout的概念:您将使用glVertexAttribPointer()和可能的 VAO。通过这种方式,您还可以以独立于 API 的方式抽象出顶点布局。

主要思想是:

PS如果您需要有关如何在 GLSL 中正确实现光照、材料的想法(我的意思是这里的算法),您最好选择一些书籍或在线教程,而不是在这里询问。或者只是谷歌搜索“GLSL 照明”。您可以找到有趣的这些链接:

快乐编码!

于 2013-10-05T06:44:36.130 回答