1. 云栖社区>
  2. PHP教程>
  3. 正文

如何把一个标准 GLSL 例程改写为 Codea shader

作者:用户 来源:互联网 时间:2017-12-01 16:12:24

标准glslshader改写一个如何Codea

如何把一个标准 GLSL 例程改写为 Codea shader - 摘要: 本文讲的是如何把一个标准 GLSL 例程改写为 Codea shader, # 如何把一个标准 GLSL 例程改写为 Codea shader## 概述这里所说的标准 `GLSL` 例程指的是 `OpenGL ES 2.0/3.0` 中使用 `GLSL` 编写, 由 `顶点着色器` 和 `片段着色器` 组成的 `



# 如何把一个标准 GLSL 例程改写为 Codea shader
## 概述
这里所说的标准 `GLSL` 例程指的是 `OpenGL ES 2.0/3.0` 中使用 `GLSL` 编写, 由 `顶点着色器` 和 `片段着色器` 组成的 `shader`. 在 `` 中有很多优秀的 `shader` 例程, 它们一般通过 `C 程序` 来调用, 我们现在希望把它们改写为 `Codea shader`, 也就是准备用 `Lua` 来调用, 以一个 `法线贴图` 的例程为例说明改写过程细节.
## 法线贴图例程
### 顶点着色器代码
下面是顶点着色器原始代码:
```
uniform mat4 u_matViewInverse;
uniform mat4 u_matViewProjection;
uniform vec3 u_lightPosition;
uniform vec3 u_eyePosition;
varying vec2 v_texcoord
varying vec3 v_viewDirection
varying vec3 v_lightDirection
attribute vec4 a_vertex;
attribute vec2 a_texcoord0;
attribute vec3 a_normal;
attribute vec3 a_binormal;
attribute vec3 a_tangent;
void main(void) {
// Transform eye vector into world space
vec3 eyePositionWorld =
(u_matViewInverse * vec4(u_eyePosition, 1.0)).xyz;
// Compute world space direction vector
vec3 viewDirectionWorld = eyePositionWorld - a_vertex.xyz;
// Transform light position into world space vec3 lightPositionWorld =
(u_matViewInverse * vec4(u_lightPosition, 1.0)).xyz;
// Compute world space light direction vector
vec3 lightDirectionWorld = lightPositionWorld - a_vertex.xyz;
// Create the tangent matrix
mat3 tangentMat = mat3(a_tangent,
a_binormal,
a_normal);

// Transform the view and light vectors into tangent space
v_viewDirection = viewDirectionWorld * tangentMat;
v_lightDirection = lightDirectionWorld * tangentMat;
// Transform output position
gl_Position = u_matViewProjection * a_vertex;
// Pass through texture coordinate
v_texcoord = a_texcoord0.xy;
}
```
我们按照代码顺序来一行行地进行转换, 首先变量声明, 有 `4` 个统一变量 `uniform`, 有 `3` 传给片段着色器的变量 `varying`, 有 `5` 个来自主程序的顶点属性 `attribute`.
我们需要处理的就是找到 `uniform` 和 `attribute` 在 `Codea` 中的对应设置, 依次进行:
### 统一变量的对应设置
4个统一变量如下:
```
uniform mat4 u_matViewInverse;
uniform mat4 u_matViewProjection;
uniform vec3 u_lightPosition;
uniform vec3 u_eyePosition;
```
按照变量名的含义:
- `u_matViewInverse` 应该是 `viewMatrix` 的逆矩阵;
- `u_matViewProjection` 应该是 `viewMatrix` 和 `projectionMatrix` 的相乘;
- `u_lightPosition` 应该是光源位置, 由我们自行设置;
- `u_eyePosition` 应该是摄像机位置, 可以通过 `camera` 来设置.
视图矩阵的逆矩阵, 在书中描述是为了把 `u_lightPosition ` 和 `u_eyePosition ` 从视图空间转换到世界空间, 那么视图矩阵就是从世界空间转换到视图空间了?
### 属性的对应设置
在 `Codea` 中, 有一些约定俗成的设置, 比如顶点的属性 `attribute`, 就有一些预先定义好的:
- `position` 顶点坐标
- `texCoord` 顶点的纹理坐标
- `color` 顶点的颜色
- `normal` 顶点的法线
在上面的顶点着色器代码中, 我们看到用了这么几个属性:
```
attribute vec4 a_vertex;
attribute vec2 a_texcoord0;
attribute vec3 a_normal;
attribute vec3 a_binormal;
attribute vec3 a_tangent;
```
很明显, 对应关系如下:
- `a_vertex` 跟 `position` 一样;
- `a_texcoord0` 跟 `texCoord` 一样;
- `a_normal` 跟 `normal` 一样.
还有两个属性 `a_binormal 次法线` 和 `a_tangent 切线`, 在 `Codea` 中没有对应的属性, 就需要我们自己计算了, 这部分可以在 `Lua` 主程序中计算, 也可以在 `shader` 中计算. 如果在 `Lua` 中计算, 那么它们的声明可以保持不变, 如果在 `shader` 中计算, 就不能声明为 `attribute` 了.
原本计划为减少调试难度, 我们决定先在 `Lua` 中计算, 确认调试通过了, 再改写为 `shader` 计算.
后来看到这篇教程 [Mesh Deformers with the GLSL](http://www.ozone3d.net/tutorials/mesh_deformer_p2.php), 给出了在 `shader` 中根据 `normal` 属性计算 `binormal` 和 `tangent` 的算法, 所以我们就直接引用一下:
```
vec3 tangent;
vec3 binormal;

vec3 c1 = cross(gl_Normal, vec3(0.0, 0.0, 1.0));
vec3 c2 = cross(gl_Normal, vec3(0.0, 1.0, 0.0));

if(length(c1)>length(c2))
{
tangent = c1;
}
else
{
tangent = c2;
}

// 归一化切线
tangent = normalize(tangent);

binormal = cross(gl_Normal, tangent);
binormal = normalize(binormal);
```
所以, 最终得到的顶点着色器代码为:
```
uniform mat4 u_matViewInverse;
uniform mat4 u_matViewProjection;
uniform vec3 u_lightPosition;
uniform vec3 u_eyePosition;
varying vec2 v_texcoord
varying vec3 v_viewDirection
varying vec3 v_lightDirection
attribute vec4 a_vertex;
attribute vec2 a_texcoord0;
attribute vec3 a_normal;
// attribute vec3 a_binormal;
// attribute vec3 a_tangent;
void main(void) {
// 先根据 normal 计算 binormal 和 tangent
vec3 tangent;
vec3 binormal;

vec3 c1 = cross(normal, vec3(0.0, 0.0, 1.0));
vec3 c2 = cross(normal, vec3(0.0, 1.0, 0.0));

if(length(c1)>length(c2))
{
tangent = c1;
}
else
{
tangent = c2;
}

// 归一化切线和次法线
tangent = normalize(tangent);
binormal = cross(normal, tangent);
binormal = normalize(binormal);
// 坐标空间转换: 摄像机坐标 Transform eye vector into world space
vec3 eyePositionWorld =
(u_matViewInverse * vec4(u_eyePosition, 1.0)).xyz;
// Compute world space direction vector
vec3 viewDirectionWorld = eyePositionWorld - a_vertex.xyz;
// Transform light position into world space
vec3 lightPositionWorld =
(u_matViewInverse * vec4(u_lightPosition, 1.0)).xyz;
// Compute world space light direction vector
vec3 lightDirectionWorld = lightPositionWorld - a_vertex.xyz;
// Create the tangent matrix
mat3 tangentMat = mat3(a_tangent,
a_binormal,
a_normal);

// Transform the view and light vectors into tangent space
v_viewDirection = viewDirectionWorld * tangentMat;
v_lightDirection = lightDirectionWorld * tangentMat;
// Transform output position
gl_Position = u_matViewProjection * a_vertex;
// Pass through texture coordinate
v_texcoord = a_texcoord0.xy;
}
```### 片段着色器代码
下面是片段着色器原始代码:
```
precision mediump float;
uniform vec4 u_ambient;
uniform vec4 u_specular;
uniform vec4 u_diffuse;
uniform float u_specularPower;
uniform sampler2D s_baseMap;
uniform sampler2D s_bumpMap;
varying vec2 v_texcoord;
varying vec3 v_viewDirection;
varying vec3 v_lightDirection;
void main(void) {
// Fetch basemap color
vec4 baseColor = texture2D(s_baseMap, v_texcoord);
// Fetch the tangent-space normal from normal map
vec3 normal = texture2D(s_bumpMap, v_texcoord).xyz;
// Scale and bias from [0, 1] to [-1, 1] and normalize
normal = normalize(normal * 2.0 - 1.0);
// Normalize the light direction and view direction
vec3 lightDirection = normalize(v_lightDirection);
vec3 viewDirection = normalize(v_viewDirection);
// Compute N.L
float nDotL = dot(normal, lightDirection);
// Compute reflection vector
vec3 reflection = (2.0 * normal * nDotL) - lightDirection;
// Compute R.V
float rDotV = max(0.0, dot(reflection, viewDirection));
// Compute Ambient term
vec4 ambient = u_ambient * baseColor;
// Compute Diffuse term
vec4 diffuse = u_diffuse * nDotL * baseColor;
// Compute Specular term
vec4 specular = u_specular * pow(rDotV, u_specularPower);
// Output final color
gl_FragColor = ambient + diffuse + specular;
}
```
这段代码不需要任何修改, 直接使用就行
## Lua 主程序
我们用来加载 `shader` 的 `Lua` 主程序如下:
```
function setup()
print("normal 3D")
tchx=0
tchy=0createMesh()
cam = vec3(0, 0, 1000)
obj = vec3(0, 0, 0)
light = vec3(tchx, tchy, 0.075)
end
function draw()
perspective(50, WIDTH/HEIGHT)
light = vec3(tchx, tchy, 0.0075)
setShaderParam()
camera(cam.x, cam.y, cam.z, obj.x, obj.y, obj.z)
m:draw()
end
function createMesh()
m = mesh()
local w,h = WIDTH,HEIGHT
local img1, img2 = readImage("Dropbox:n1"), readImage("Dropbox:n2")
m:addRect(w/2,h/2,w,h)
-- m:addRect(0,0,w,h)---[[
m.shader = shader(shader2.vs,shader2.fs)
m.shader.u_matViewInverse = viewMatrix():inverse()
m.shader.u_matViewProjection = viewMatrix() * projectionMatrix()m.shader.s_baseMap = img1
m.shader.s_bumpMap = img2
--]]--[[
m.shader.u_lightPosition = light or vec3(100, 100, 100)
m.shader.u_eyePosition = cam or vec3(100, 100, 100)
-- ambient/specular/diffuse 环境光,反射光,散射
m.shader.u_ambient = vec4(0.15, 0.15, 0.15, 0.8)
m.shader.u_specular = vec4(0.05, 0.05, 0.05, 0.8)
m.shader.u_diffuse = vec4(0.105, 0.005, 0.005, 0.8)
m.shader.u_specularPower = 0.09
--]]-- m.texture = img1
end
function setShaderParam()
m.shader.u_lightPosition = light or vec3(100, 100, 100)
m.shader.u_eyePosition = cam or vec3(100, 100, 100)
-- ambient/specular/diffuse 环境光,反射光,散射
m.shader.u_ambient = vec4(0.37,0.37,0.37,1.0)
m.shader.u_specular = vec4(0.5,0.5,0.5,1.0)
m.shader.u_diffuse = vec4(0.88,0.88,0.88,1.0)
m.shader.u_specularPower = .05
end
function touched(touch)
if touch.state == BEGAN or touch.state == MOVING then
tchx=touch.x+50
tchy=touch.y+50
end
end
```
## 问题
### 1 无法改变距离
发现无法通过设置 `camera` 函数的参数来改变视角, 找了半天, 发现是顶点着色器中这条语句的原因;
```
gl_Position = u_matViewProjection * a_vertex;
```
它把视图投影矩阵应用于顶点, 变换后得到视图投影, 而不是常用的模型视图投影, 可以将其修改为:
```
uniform mat4 modelViewProjection;
gl_Position = modelViewProjection * a_vertex;
```
这样就可以调节摄像机和物体之间的距离了.
### 2 上下坐标错位
具体来说就是点击上方, 本来应该把光源放在上方, 结果下方出现高光, 说明上下坐标错位, 在我们的屏幕上也就是 `y` 轴坐标错位, 这一点可能是因为 `Codea` 使用了不同手系的坐标导致, 可以在顶点着色器中通过乘一个如下的矩阵来调整:
```
mat4 mm = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, -1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0.
0.0, 0.0, 0.0, 1.0);

gl_Position = mm * modelViewProjection * a_vertex;
```
### 3 光线没有设置衰减
不设置衰减的光线会导致光源越远, 物体越亮的错误情况, 增加一个衰减系数就可以了
## 参考
[Mesh Deformers with the GLSL](http://www.ozone3d.net/tutorials/mesh_deformer_p2.php)



以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索标准 , glsl , shader , 改写 , 一个 , 如何 Codea ,以便于您获取更多的相关知识。