iPad GLSL。从片段着色器中如何获取表面(而不是顶点)法线

发布于 2024-08-26 15:36:37 字数 172 浏览 7 评论 0原文

是否可以从片段着色器内访问表面法线(与片段平面相关的法线)?或者也许这可以在顶点着色器中完成?

当我们沿着着色器管道走下去时,相关几何体的所有知识是否都会丢失,或者是否有一些巧妙的方法可以在片段着色器的顶点中恢复该信息?

提前致谢。

干杯,
道格
推特:@dugla

Is it possible to access the surface normal - the normal associated with the plane of a fragment - from within a fragment shader? Or perhaps this can be done in the vertex shader?

Is all knowledge of the associated geometry lost when we go down the shader pipeline or is there some clever way of recovering that information in either the vertex of fragment shader?

Thanks in advance.

Cheers,
Doug
twitter: @dugla

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

澜川若宁 2024-09-02 15:36:39

您只需使用“变化”(在较新的 OpenGL 中只是输入/输出)变量即可从顶点法线插值得到每像素法线。但不要忘记将这种常态标准化!插值法线的长度不得再为 1。这些法线在锐边上也会产生不好的结果。

如果您想使用更高分辨率的自定义法线,常用的技术是法线贴图。您可以为对象创建带有烘焙法线的纹理。然后,您可以使用纹理查找来访问片段纹理中的法线。

You can get per-pixel normals interpolated from vertex normales by just using a "varying" (in newer OpenGL it is just in/out) variable. But do not forget to normalize this normal! Interpolated normals must not have a length of 1 any longer. These normals also give bad results on sharp edges.

If you want to use custom normals with a higher resolution a commonly used technique are normal maps. You create a texture with baked normals for your object. Then you can access the normal in the fragment texture using a textur look-up.

尐偏执 2024-09-02 15:36:39

如果您将顶点法线以“变化”的方式传递给片段着色器,那么您将获得插值的片段法线。

编辑:您必须计算应用程序中的法线,并将它们作为三角形每个顶点的属性传递到着色器中。

计算三角形法线的常用方法是使用叉积。

  1. 称构成三角形的三点为 P1、P2 和 P3。
  2. 计算 V1,即从 P1 到 P2 的向量。
  3. 计算 V2,即从 P1 到 P3 的向量。
  4. 计算 V1 和 V2 的叉积。

这将为您提供三角形平面的法线。 V2 应该位于 V1 的“左侧”,否则您的法线将指向“内”而不是“外”。有关详细信息,请参阅有关交叉产品的维基百科文章

进一步编辑:好的,我现在明白你的问题了。是的,确实,对于共享顶点,每个顶点实际上不能有多个法线。

我唯一能想到的另一件事是,也许几何着色器可以提供帮助,因为它会传递三角形的所有三个顶点。不过我对他们没有任何经验。

If you pass the vertex normal through to the fragment shader in a "varying" then you will get an interpolated fragment normal.

EDIT: You will have to calculate the normals in your application, and pass them into your shader as an attribute for each vertex of your triangle.

The usual way to calculate the normal for a triangle is with a cross product.

  1. Call the three points making up the triangle P1, P2, and P3.
  2. Calculate V1, the vector from P1 to P2.
  3. Calculate V2, the vector from P1 to P3.
  4. Calculate the cross product of V1 and V2.

This will give you the normal to the plane of the triangle. V2 should be "to the left of" V1, or your normal will point "in" instead of "out". See the Wikipedia article on cross products for details.

FURTHER EDIT: Right, I understand your problem now. Yes, it's true that with shared vertices you can't really have more than one normal per vertex.

The only other thing that I can think of is that maybe a geometry shader could help, because it gets passed all three vertices for a triangle. I don't have any experience with them though.

影子的影子 2024-09-02 15:36:38

表面法线向量可以通过片段着色器中视图空间位置的偏导数来近似计算。偏导数可以通过函数 获得dFdx 和 dFdy。为此,需要 OpenGL es 3.0 或 OES_standard_derivatives 扩展:

in vec3 view_position;

void main()
{
    vec3 normalvector = cross(dFdx(view_position), dFdy(view_position));
    nv = normalize(normalvector * sign(normalvector.z));

    .....
}

一般来说,可以在几何着色器中计算表面的法线向量(自 OpenGL ES 3.2 起)。
例如,如果您绘制三角形,您将在几何着色器中获得所有三个点。
三个点定义一个平面,可以从该平面计算法向量。
您只需要小心这些点是顺时针还是逆时针排列。

三角形的法向量是定义的 2 个向量的归​​一化叉积
由三角形的角点。
请参阅以下逆时针三角形的示例:

顶点着色器 几何

#version 400

layout (location = 0) in vec3 inPos;

out vec3 vertPos;

uniform mat4 u_projectionMat44;
uniform mat4 u_modelViewMat44;

void main()
{
    vec4 viewPos = u_modelViewMat44 * vec4( inPos, 1.0 );
    vertPos = viewPos.xyz;
    gl_Position = u_projectionMat44 * viewPos;
}

着色器

#version 400

layout( triangles ) in;
layout( triangle_strip, max_vertices = 3 ) out;

in vec3 vertPos[];

out vec3 geoPos;
out vec3 geoNV;

void main()
{
    vec3 leg1 = vertPos[1] - vertPos[0];
    vec3 leg2 = vertPos[2] - vertPos[0];
    geoNV = normalize( cross( leg1, leg2 ) ); 

    geoPos = vertPos[0];
    EmitVertex();
    geoPos = vertPos[1];
    EmitVertex();
    geoPos = vertPos[2];
    EmitVertex();
    EndPrimitive();
}

片段着色器

#version 400

in vec3 geoPos;
in vec3 geoNV;

void main()
{
    // ...
}

当然,您也可以在曲面细分着色器中计算法线向量(自 OpenGL ES 3.2 起)。
但是,只有当您出于其他原因需要曲面细分着色器并另外计算时,这才有意义
面的法线向量:

顶点着色器

顶点着色器同上。

曲面细分控制着色器 曲面

#version 400

layout( vertices=3 ) out;

in  vec3 vertPos[];
out vec3 tctrlPos[];

void main()
{
    tctrlPos[gl_InvocationID] = vertPos[gl_InvocationID];

    if ( gl_InvocationID == 0 )
    {
        gl_TessLevelOuter[0] =  ;
        gl_TessLevelOuter[1] =  ;
        gl_TessLevelOuter[2] =  ;
        gl_TessLevelInner[0] =  ;
    }
}

细分评估着色器

#version 400

layout(triangles, ccw) in;

in vec3 tctrlPos[];

out vec3 tevalPos;
out vec3 tevalNV;

void main()
{
  vec3 leg1 = tctrlPos[1] - tctrlPos[0];
  vec3 leg2 = tctrlPos[2] - tctrlPos[0];
  tevalNV = normalize( cross( leg1, leg2 ) ); 

  tevalPos = tctrlPos[0] * gl_TessCoord.x + tctrlPos[1] * gl_TessCoord.y + tctrlPos[2] * gl_TessCoord.z;
}

片段着色器

#version 400

in vec3 tevalPos;
in vec3 tevalNV;

void main()
{
    // ...
}

The surface normal vector can be calculated approximately by the partial derivative of the view space position in the frgament shader. The partial derivative can be get by the functions dFdx and dFdy. For this is required OpenGL es 3.0 or the OES_standard_derivatives extension:

in vec3 view_position;

void main()
{
    vec3 normalvector = cross(dFdx(view_position), dFdy(view_position));
    nv = normalize(normalvector * sign(normalvector.z));

    .....
}

In general it is possible to calculate the normal vector of a surface in a geometry shader (since OpenGL ES 3.2).
For example if you draw triangles you get all three points in the geometry shader.
Three points define a plane from which the normal vector can be calculated.
You just have to be careful if the points are arranged clockwise or counterclockwise.

The normal vector of a triangle is the normalized cross product of 2 vectors defined
by the corner points of the triangle.
See the folowing example which for counterclockwise triangles:

Vertex shader

#version 400

layout (location = 0) in vec3 inPos;

out vec3 vertPos;

uniform mat4 u_projectionMat44;
uniform mat4 u_modelViewMat44;

void main()
{
    vec4 viewPos = u_modelViewMat44 * vec4( inPos, 1.0 );
    vertPos = viewPos.xyz;
    gl_Position = u_projectionMat44 * viewPos;
}

Geometry shader

#version 400

layout( triangles ) in;
layout( triangle_strip, max_vertices = 3 ) out;

in vec3 vertPos[];

out vec3 geoPos;
out vec3 geoNV;

void main()
{
    vec3 leg1 = vertPos[1] - vertPos[0];
    vec3 leg2 = vertPos[2] - vertPos[0];
    geoNV = normalize( cross( leg1, leg2 ) ); 

    geoPos = vertPos[0];
    EmitVertex();
    geoPos = vertPos[1];
    EmitVertex();
    geoPos = vertPos[2];
    EmitVertex();
    EndPrimitive();
}

Fragment shader

#version 400

in vec3 geoPos;
in vec3 geoNV;

void main()
{
    // ...
}

Of course you can calculate the normalvector also in the tesselation shaders (since OpenGL ES 3.2).
But this makes sense only if you already required tessellation shader for other reasons and additionally calculate
the normal vector of the face:

Vertex shader

The vertex shader is the same as above.

Tessellation control shader

#version 400

layout( vertices=3 ) out;

in  vec3 vertPos[];
out vec3 tctrlPos[];

void main()
{
    tctrlPos[gl_InvocationID] = vertPos[gl_InvocationID];

    if ( gl_InvocationID == 0 )
    {
        gl_TessLevelOuter[0] =  ;
        gl_TessLevelOuter[1] =  ;
        gl_TessLevelOuter[2] =  ;
        gl_TessLevelInner[0] =  ;
    }
}

Tessellation evaluation shader

#version 400

layout(triangles, ccw) in;

in vec3 tctrlPos[];

out vec3 tevalPos;
out vec3 tevalNV;

void main()
{
  vec3 leg1 = tctrlPos[1] - tctrlPos[0];
  vec3 leg2 = tctrlPos[2] - tctrlPos[0];
  tevalNV = normalize( cross( leg1, leg2 ) ); 

  tevalPos = tctrlPos[0] * gl_TessCoord.x + tctrlPos[1] * gl_TessCoord.y + tctrlPos[2] * gl_TessCoord.z;
}

Fragmant shader

#version 400

in vec3 tevalPos;
in vec3 tevalNV;

void main()
{
    // ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文