在 UV 坐标中查找屏幕像素的大小以供片段着色器使用

Finding the size of a screen pixel in UV coordinates for use by the fragment shader

我有一个非常详细的纹理(我在片段着色器中使用假色查找渲染的假色信息)。我的问题是,有时用户会放大到远离该纹理的地方,并且会丢失精细的细节:看不到纹理中的细线。我想修改我的代码,使这些行弹出。

我的想法是我可以 运行 快速过滤相邻的文本元素并挑选出 biggest/smallest/most 有趣的值进行渲染。我不确定该怎么做是找出是否(以及多少)这样做。当用户放大到三角形时,我想要标准查找。当它们缩小时,屏幕上的单个像素映射到许多纹理像素。

我如何得到这个的估计值?我正在使用正交和透视相机进行此操作。

我的想法是我可以以某种方式使用顶点着色器来估计一个屏幕像素在 UV 中有多大 space 并将其作为变量传递给片段着色器,但我仍然不这样做对转换和 space 没有足够的把握来理解这个想法。

我目前的顶点着色器非常简单:

  varying vec2 vUv;
  varying vec3 vPosition;
  varying vec3 vNormal;
  varying vec3 vViewDirection;

   void main() {
       vUv = uv;
       vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
       vPosition = (modelMatrix *
           vec4(position,1.0)).xyz;
       gl_Position = projectionMatrix * mvPosition;
      vec3 transformedNormal = normalMatrix *  vec3( normal );
      vNormal = normalize( transformedNormal );
      vViewDirection = normalize(mvPosition.xyz);
   }

我如何获得像 vDeltaUV 这样的东西,它以 UV 单位给出屏幕像素之间的距离?

约束:我在 three.js 内部使用 WebGL 工作。

这是一张图片的示例,其中用户将视角放大到接近我的纹理:

这是同一个例子,但缩小了;上面的特征是靠近中心的一条几乎看不见的对角线(查看坐标以获得比例感)。我希望通过使用相应文本元素数组的最红颜色渲染所有像素来弹出这条线。

附录(关于 LJ 的评论)... 不,我不认为 mipmapping 会做我想做的事,有两个原因。

首先,我实际上并没有映射纹理;也就是说,我正在做这样的事情:

   gl_FragColor = texture2D(mappingtexture,  texture2d(vec2(inputtexture.g,inputtexture.r))

用户动态创建贴图纹理,这让我可以实时改变假色贴图。我认为这对我的应用程序来说实际上是一个非常优雅的解决方案。

其次,我不想绘制相邻像素的平均值(即平滑)我想要相邻像素的最极端值(即更类似于边缘查找的东西)。在这种情况下,"Extreme" 在技术上是由我对输入纹理中的 g/r 颜色值进行编码定义的。

解决方法: 感谢下面的回答,我现在有了一个可行的解决方案。

在我的 javascript 代码中,我必须添加:

  extensions: {derivatives: true}

我对 ShaderMaterial 的声明。然后在我的片段着色器中:

   float dUdx = dFdx(vUv.x); // Difference in U between this pixel and the one to the right.
  float dUdy = dFdy(vUv.x); // Difference in U between this pixel and the one to the above.
  float dU = sqrt(dUdx*dUdx + dUdy*dUdy);
  float pixel_ratio = (dU*(uInputTextureResolution));

这让我可以做这样的事情:

   float x = ... the u coordinate in pixels in the input texture
   float y = ... the v coordinate in pixels in the input texture
   vec4 inc = get_encoded_adc_value(x,y);

   // Extremum mapping:
   if(pixel_ratio>2.0) {
    inc = most_extreme_value(inc, get_encoded_adc_value(x+1.0, y));
   }
   if(pixel_ratio>3.0) {
    inc = most_extreme_value(inc, get_encoded_adc_value(x-1.0, y));    
  }

效果很微妙,但绝对有!线条更加清晰。

感谢您的帮助!

你不能在顶点着色器中这样做,因为它是预光栅化阶段,因此输出分辨率不可知,但在片段着色器中你可以使用 dFdx, dFdy and fwidth 使用 GL_OES_standard_derivatives 扩展(这是几乎随处可用)来估计采样足迹。

如果您不实时更新纹理,一个更简单、更有效的解决方案是在 CPU 上为其生成自定义 mip 级别。