我正在尝试实施 GLSL 着色器,该着色器会突出显示渲染的 3D 网格的外边缘。问题是我无权访问 OpenGL 客户端代码,因此必须仅在 GLSL 着色器中执行此操作。
我的第一次尝试是使用/采用 Unity 中的this着色器,并在 OpenGL GLSL 中执行此操作。它应该如何看待:
这就是我得到的:
我不确定如果我正确地计算了这些东西,但是你可以看到输出远远超出我的预期。
这是食人魔材料
material Chassis
{
technique
{
pass standard
{
cull_software back
scene_blend zero one
}
pass psssm
{
cull_software front
scene_blend src_alpha one_minus_src_alpha
vertex_program_ref reflection_cube_specularmap_normalmap_vs100
{
param_named_auto modelViewProjectionMatrix worldviewproj_matrix
param_named_auto normalMatrix inverse_transpose_world_matrix
param_named_auto modelView worldview_matrix
param_named_auto camera_world_position camera_position
param_named_auto inverse_projection_matrix inverse_projection_matrix
param_named_auto projection_matrix projection_matrix
param_named_auto p_InverseModelView inverse_worldview_matrix
}
fragment_program_ref reflection_cube_specularmap_normalmap_fs100
{
}
}
}
}
这是顶点着色器
#version 140
#define lowp
#define mediump
#define highp
in vec4 vertex;
in vec3 normal;
uniform mat4 normalMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelView;
uniform vec3 camera_world_position;
uniform mat4 projection_matrix;
uniform mat4 inverse_projection_matrix;
void main()
{
vec4 pos = modelViewProjectionMatrix * vertex;
mat4 modelView = inverse_projection_matrix * modelViewProjectionMatrix;
vec4 norm = inverse(transpose(modelView)) * vec4(normal, 0.0);
vec2 offset = vec2( norm.x * projection_matrix[0][0], norm.y * projection_matrix[1][1] );
pos.xy += offset * pos.z * 0.18;
gl_Position = pos;
}
编辑:我添加了食人魔使用的材质脚本,并添加了顶点着色器代码。
答案 0 :(得分:4)
我假设单个复杂的 3D 网格。我会用2遍渲染来做到这一点:
清除屏幕
让(0,0,0)
使用清晰的颜色。
渲染网格
禁用深度输出,测试(或之后清除)。不要仅使用某些预定义颜色使用着色填充,例如(1,1,1)
让我们为简单的多维数据集执行此操作:
读取帧缓冲区并将其用作纹理
所以要么使用 FBO 并渲染到#1,#2 的纹理,要么使用glReadPixels
并将其作为一些纹理加载回 GPU (我知道它速度较慢,但也适用于英特尔)。有关详细信息,请参阅此处的答案:
清除背景色的屏幕
<强>呈现强>
所以要么渲染GL_QUAD
覆盖整个屏幕,要么用阴影渲染你的网格以及你想要的东西。您还需要将上一步中的纹理传递给 GLSL 。
在片段中照常渲染...但最后还要添加:
扫描当前片段屏幕位置周围的所有纹素,其距离等于上一步中纹理中的轮廓粗细。如果在其中找到任何黑色像素,则使用轮廓颜色覆盖输出的颜色。你甚至可以用最小的黑色调制它。
这与此非常相似:
但更简单。结果如下:
我采用了我的Analysis of a shader in VR示例并将其转换为:
<强>片段:强>
// Fragment
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location =64) uniform vec3 lt_pnt_pos;// point light source position [GCS]
layout(location =67) uniform vec3 lt_pnt_col;// point light source color&strength
layout(location =70) uniform vec3 lt_amb_col;// ambient light source color&strength
in vec3 LCS_pos; // fragment position [LCS]
in vec3 pixel_pos; // fragment position [GCS]
in vec3 pixel_col; // fragment surface color
in vec3 pixel_nor; // fragment surface normal [GCS]
out vec4 col;
// outline
uniform sampler2D txr; // texture from previous pass
uniform int thickness; // [pixels] outline thickness
uniform float xs,ys; // [pixels] texture/screen resolution
void main()
{
// standard rendering
float li;
vec3 c,lt_dir;
lt_dir=normalize(lt_pnt_pos-pixel_pos); // vector from fragment to point light source in [GCS]
li=dot(pixel_nor,lt_dir);
if (li<0.0) li=0.0;
c=pixel_col*(lt_amb_col+(lt_pnt_col*li));
// outline effect
if (thickness>0) // thickness effect in second pass
{
int i,j,r=thickness;
float xx,yy,rr,x,y,dx,dy;
dx=1.0/xs; // texel size
dy=1.0/ys;
x=gl_FragCoord.x*dx;
y=gl_FragCoord.y*dy;
rr=thickness*thickness;
for (yy=y-(float(thickness)*dy),i=-r;i<=r;i++,yy+=dy)
for (xx=x-(float(thickness)*dx),j=-r;j<=r;j++,xx+=dx)
if ((i*i)+(j*j)<=rr)
if ((texture(txr,vec2(xx,yy)).r)<0.01)
{
c=vec3(1.0,0.0,0.0); // outline color
i=r+r+1;
j=r+r+1;
break;
}
}
else c=vec3(1.0,1.0,1.0); // render with white in first pass
// output color
col=vec4(c,1.0);
}
顶点着色器无变化:
// Vertex
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location = 0) in vec3 pos;
layout(location = 2) in vec3 nor;
layout(location = 3) in vec3 col;
layout(location = 0) uniform mat4 m_model; // model matrix
layout(location =16) uniform mat4 m_normal; // model matrix with origin=(0,0,0)
layout(location =32) uniform mat4 m_view; // inverse of camera matrix
layout(location =48) uniform mat4 m_proj; // projection matrix
out vec3 LCS_pos; // fragment position [LCS]
out vec3 pixel_pos; // fragment position [GCS]
out vec3 pixel_col; // fragment surface color
out vec3 pixel_nor; // fragment surface normal [GCS]
void main()
{
LCS_pos=pos;
pixel_col=col;
pixel_pos=(m_model*vec4(pos,1)).xyz;
pixel_nor=(m_normal*vec4(nor,1)).xyz;
gl_Position=m_proj*m_view*m_model*vec4(pos,1);
}
CPU 端代码如下所示:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
GLfloat lt_pnt_pos[3]={+2.5,+2.5,+2.5};
GLfloat lt_pnt_col[3]={0.8,0.8,0.8};
GLfloat lt_amb_col[3]={0.2,0.2,0.2};
GLuint txrid=0;
GLfloat animt=0.0;
//---------------------------------------------------------------------------
// https://stackoverflow.com/q/46603878/2521214
//---------------------------------------------------------------------------
void gl_draw()
{
// load values into shader
GLint i,id;
GLfloat m[16];
glUseProgram(prog_id);
GLfloat x,y,z,d=0.25;
id=glGetUniformLocation(prog_id,"txr"); glUniform1i(id,0);
id=glGetUniformLocation(prog_id,"xs"); glUniform1f(id,xs);
id=glGetUniformLocation(prog_id,"ys"); glUniform1f(id,ys);
id=64; glUniform3fv(id,1,lt_pnt_pos);
id=67; glUniform3fv(id,1,lt_pnt_col);
id=70; glUniform3fv(id,1,lt_amb_col);
glGetFloatv(GL_MODELVIEW_MATRIX,m);
id=0; glUniformMatrix4fv(id,1,GL_FALSE,m);
m[12]=0.0; m[13]=0.0; m[14]=0.0;
id=16; glUniformMatrix4fv(id,1,GL_FALSE,m);
for (i=0;i<16;i++) m[i]=0.0; m[0]=1.0; m[5]=1.0; m[10]=1.0; m[15]=1.0;
id=32; glUniformMatrix4fv(id,1,GL_FALSE,m);
glGetFloatv(GL_PROJECTION_MATRIX,m);
id=48; glUniformMatrix4fv(id,1,GL_FALSE,m);
// draw VAO cube (no outline)
id=glGetUniformLocation(prog_id,"thickness"); glUniform1i(id,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
vao_draw(); // render cube
// copy frame buffer to CPU memory and than back to GPU as Texture
BYTE *map=new BYTE[xs*ys*4];
glReadPixels(0,0,xs,ys,GL_RGB,GL_UNSIGNED_BYTE,map); // framebuffer -> map[]
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, xs, ys, 0, GL_RGB, GL_UNSIGNED_BYTE, map); // map[] -> texture txrid
delete[] map;
// draw VAO cube (outline)
id=glGetUniformLocation(prog_id,"thickness"); glUniform1i(id,5);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
vao_draw(); // render cube
glDisable(GL_TEXTURE_2D);
// turn of shader
glUseProgram(0);
// rotate the cube to see animation
glMatrixMode(GL_MODELVIEW);
// glRotatef(1.0,0.0,1.0,0.0);
// glRotatef(1.0,1.0,0.0,0.0);
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
gl_init(Handle);
glGenTextures(1,&txrid);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txrid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_COPY);
glDisable(GL_TEXTURE_2D);
int hnd,siz; char vertex[4096],fragment[4096];
hnd=FileOpen("normal_shading.glsl_vert",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,vertex ,siz); vertex [siz]=0; FileClose(hnd);
hnd=FileOpen("normal_shading.glsl_frag",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,fragment,siz); fragment[siz]=0; FileClose(hnd);
glsl_init(vertex,fragment);
// hnd=FileCreate("GLSL.txt"); FileWrite(hnd,glsl_log,glsl_logs); FileClose(hnd);
int i0,i;
mm_log->Lines->Clear();
for (i=i0=0;i<glsl_logs;i++)
if ((glsl_log[i]==13)||(glsl_log[i]==10))
{
glsl_log[i]=0;
mm_log->Lines->Add(glsl_log+i0);
glsl_log[i]=13;
for (;((glsl_log[i]==13)||(glsl_log[i]==10))&&(i<glsl_logs);i++);
i0=i;
}
if (i0<glsl_logs) mm_log->Lines->Add(glsl_log+i0);
vao_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
glDeleteTextures(1,&txrid);
gl_exit();
glsl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
gl_resize(ClientWidth,ClientHeight-mm_log->Height);
glMatrixMode(GL_PROJECTION);
glTranslatef(0,0,-15.0);
glMatrixMode(GL_MODELVIEW);
glRotatef(-15.0,0.0,1.0,0.0);
glRotatef(-125.0,1.0,0.0,0.0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
gl_draw();
animt+=0.02; if (animt>1.5) animt=-0.5;
Caption=animt;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
GLfloat dz=2.0;
if (WheelDelta<0) dz=-dz;
glMatrixMode(GL_PROJECTION);
glTranslatef(0,0,dz);
gl_draw();
}
//---------------------------------------------------------------------------
像往常一样,代码使用/基于此:
<强> [注释] 强>
如果您有多个对象,则在#2 中为每个对象使用不同的颜色。然后在#5 中扫描任何不同的颜色,然后扫描当前位置的纹素,而不是扫描黑色。
这也可以在2D图像上完成,而不是使用网格。你只需要知道背景颜色。所以你也可以使用预渲染/抓取/截图图像。
您可以添加discard
和/或更改最终if
逻辑以更改行为(就像您只想要轮廓而内部没有网格等...)。或者你可以添加轮廓颜色来渲染颜色,而不是直接指定它来获得高亮的印象......而不是着色
参见a),b),c)修改片段中的选项:
// Fragment
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location =64) uniform vec3 lt_pnt_pos;// point light source position [GCS]
layout(location =67) uniform vec3 lt_pnt_col;// point light source color&strength
layout(location =70) uniform vec3 lt_amb_col;// ambient light source color&strength
in vec3 LCS_pos; // fragment position [LCS]
in vec3 pixel_pos; // fragment position [GCS]
in vec3 pixel_col; // fragment surface color
in vec3 pixel_nor; // fragment surface normal [GCS]
out vec4 col;
// outline
uniform sampler2D txr; // texture from previous pass
uniform int thickness; // [pixels] outline thickness
uniform float xs,ys; // [pixels] texture/screen resolution
void main()
{
// standard rendering
float li;
vec3 c,lt_dir;
lt_dir=normalize(lt_pnt_pos-pixel_pos); // vector from fragment to point light source in [GCS]
li=dot(pixel_nor,lt_dir);
if (li<0.0) li=0.0;
c=pixel_col*(lt_amb_col+(lt_pnt_col*li));
// outline effect
if (thickness>0) // thickness effect in second pass
{
int i,j,r=thickness;
float xx,yy,rr,x,y,dx,dy;
dx=1.0/xs; // texel size
dy=1.0/ys;
x=gl_FragCoord.x*dx;
y=gl_FragCoord.y*dy;
rr=thickness*thickness;
for (yy=y-(float(thickness)*dy),i=-r;i<=r;i++,yy+=dy)
for (xx=x-(float(thickness)*dx),j=-r;j<=r;j++,xx+=dx)
if ((i*i)+(j*j)<=rr)
if ((texture(txr,vec2(xx,yy)).r)<0.01)
{
c =vec3(1.0,0.0,0.0); // a) assign outline color
// c+=vec3(1.0,0.0,0.0); // b) add outline color
i=r+r+1;
j=r+r+1;
r=0;
break;
}
// if (r!=0) discard; // c) do not render inside
}
else c=vec3(1.0,1.0,1.0); // render with white in first pass
// output color
col=vec4(c,1.0);
}
[Edit1]光滑边缘的单遍方法
由于您无法访问客户端代码,因此此方法仅适用于着色器。对于平滑(弯曲)边缘形状,曲面法线几乎垂直于摄像机视轴(z
)。所以它们之间的dot
接近于零。这可以直接利用...这里着色器的更新:
<强>顶点强>
// Vertex
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location = 0) in vec3 pos;
layout(location = 2) in vec3 nor;
layout(location = 3) in vec3 col;
layout(location = 0) uniform mat4 m_model; // model matrix
layout(location =16) uniform mat4 m_normal; // model matrix with origin=(0,0,0)
layout(location =32) uniform mat4 m_view; // inverse of camera matrix
layout(location =48) uniform mat4 m_proj; // projection matrix
out vec3 pixel_pos; // fragment position [GCS]
out vec3 pixel_col; // fragment surface color
out vec3 pixel_nor; // fragment surface normal [GCS]
out vec3 view_nor; // surface normal in camera [LCS]
void main()
{
pixel_col=col;
pixel_pos=(m_model*vec4(pos,1)).xyz;
pixel_nor=(m_normal*vec4(nor,1)).xyz;
mat4 m;
m=m_model*m_view; // model view matrix
m[3].xyz=vec3(0.0,0.0,0.0); // with origin set to (0,0,0)
view_nor=(m*vec4(nor,1.0)).xyz; // object local normal to camera local normal
gl_Position=m_proj*m_view*m_model*vec4(pos,1);
}
<强>片段强>
// Fragment
#version 400 core
#extension GL_ARB_explicit_uniform_location : enable
layout(location =64) uniform vec3 lt_pnt_pos;// point light source position [GCS]
layout(location =67) uniform vec3 lt_pnt_col;// point light source color&strength
layout(location =70) uniform vec3 lt_amb_col;// ambient light source color&strength
in vec3 pixel_pos; // fragment position [GCS]
in vec3 pixel_col; // fragment surface color
in vec3 pixel_nor; // fragment surface normal [GCS]
out vec4 col;
// outline
in vec3 view_nor; // surface normal in camera [LCS]
void main()
{
// standard rendering
float li;
vec3 c,lt_dir;
lt_dir=normalize(lt_pnt_pos-pixel_pos); // vector from fragment to point light source in [GCS]
li=dot(pixel_nor,lt_dir);
if (li<0.0) li=0.0;
c=pixel_col*(lt_amb_col+(lt_pnt_col*li));
// outline effect
if (abs(dot(view_nor,vec3(0.0,0.0,1.0)))<=0.5) c=vec3(1.0,0.0,0.0);
// output color
col=vec4(c,1.0);
}
这里预览:
正如你所看到的,它适用于光滑的物体,但对于像立方体这样的尖锐边缘,这根本不起作用......你可以使用与之前方法相同的组合(a,b,c
)。
m
保存模型视图矩阵,其原点设置为(0,0,0)
。这使它能够进行矢量转换(无翻译)。有关详细信息,请参阅Understanding 4x4 homogenous transform matrices。
点积结果0.5
中的if
是轮廓的粗细。 0.0
表示没有大纲,1.0
表示整个对象都是大纲。