├── README.md ├── Record-Article-1-cn.md ├── Record-Article-1-en.md ├── Record-Article-2-cn.md ├── fragmentShader.c ├── main.lua └── vertexShader.c /README.md: -------------------------------------------------------------------------------- 1 | # ShaderDebugger: A tool function for shader debugging 2 | 3 | ## Intro 4 | 5 | [`Codea`](codea.io) supports the coder to write and run the `OpenGL ES shader` code on `iPad` directlly, it is very useful for designing `3D` program, but the vars in `shader` can not be wathched directly, it makes the debug hardly. Because of this I try to make a tool function to show the value of var in `shader` outpu th screen, a proptype, it is simple, bad performance, but can work. With it we can watch the var in `shader` easily. 6 | 7 | ## About the functions 8 | 9 | In fact, they are a few global vars and functions: 10 | 11 | * Use for display the value of var `showFloat()` 12 | * Use for generate the font `ledRectChar()` 13 | * Use for judge the rect area `inRect()` 14 | 15 | ## Usage: 16 | 17 | Copy the functions into the code of `fragment shader`, then run the `showFloat()` in `main()`, like below: 18 | 19 | ### Show the int 20 | 21 | Positive int 22 | 23 | ``` 24 | shoufloat(float(2345)); 25 | ``` 26 | 27 | Negative int 28 | 29 | ``` 30 | shoufloat(float(2345)); 31 | ``` 32 | 33 | ### Show the float 34 | 35 | Float 36 | 37 | ``` 38 | shoufloat(.2345)); 39 | ``` 40 | 41 | ``` 42 | shoufloat(1234.345); 43 | ``` 44 | 45 | ``` 46 | shoufloat(-1234.345); 47 | ``` 48 | 49 | ### Show the zero 50 | 51 | ``` 52 | shoufloat(.0); 53 | ``` 54 | 55 | ## Screeshot 56 | 57 | * showFloat(float(-1234)); 58 | 59 | ![-1234](https://static.oschina.net/uploads/img/201604/02012716_WSKI.png "在这里输入图片标题") 60 | 61 | * showFloat(2097152.411); 62 | 63 | ![without fract](https://static.oschina.net/uploads/img/201604/02012812_2yQ2.png "在这里输入图片标题") 64 | 65 | * showFloat(2097152.11); 66 | 67 | ![with fract](https://static.oschina.net/uploads/img/201604/02012855_bQRE.png "在这里输入图片标题") 68 | 69 | ## Other Info 70 | 71 | Articles record the whole development: from an idea to a available propotype 72 | 73 | [How to watch a var in shader? -A new solution: change the question from 'What is' to 'Is it'](https://github.com/FreeBlues/ShaderDebugger/blob/master/Record-Article-1-en.md) 74 | [How to watch a var in shader? -A new solution: change the question from 'What is' to 'Is it'](https://github.com/FreeBlues/ShaderDebugger/blob/master/Record-Article-2-en.md) 75 | 76 | --- 77 | 78 | # ShaderDebugger: 着色器调试工具函数 79 | 80 | ## 介绍 81 | 82 | [`Codea`](codea.io) 支持在 `iPad` 上直接编写 `OpenGL ES` 的 `shader` 程序, 对于设计 `3D` 程序而已非常方便, 但是 `shader` 内部的变量无法直接观察, 使得调试起来比较困难. 鉴于此, 试着写了一个可以把 `shader` 内部变量值输出到屏幕的工具, 类似原型的东西, 比较粗糙, 性能很差, 不过刚好能用, 利用它可以轻松地观察 `shader` 内变量的值. 83 | 84 | ## 工具函数说明 85 | 86 | 实际上就是几个函数, 包括: 87 | 88 | * 用于最终显示变量值的 `showFloat()` 89 | * 用于生成所需字型的 `ledRectChar()` 90 | * 用于判断矩形区域范围的 `inRect()` 91 | 92 | ## 使用方法 93 | 94 | 把这几个函数的声明和实现拷贝到 `fragment shader` 的代码中, 直接在 `main` 函数中调用即可, 如下所示: 95 | 96 | ### 显示整数 97 | 98 | 正整数 99 | 100 | ``` 101 | shoufloat(float(2345)); 102 | ``` 103 | 104 | 负整数 105 | ``` 106 | shoufloat(float(2345)); 107 | ``` 108 | 109 | ### 显示浮点数 110 | 111 | 浮点数 112 | 113 | ``` 114 | shoufloat(.2345)); 115 | ``` 116 | 117 | ``` 118 | shoufloat(1234.345); 119 | ``` 120 | 121 | ``` 122 | shoufloat(-1234.345); 123 | ``` 124 | 125 | ### 显示0 126 | 127 | ``` 128 | shoufloat(.0); 129 | ``` 130 | 131 | ## 其他 132 | 133 | 记录完成这个原型工具的整个开发调试过程的思路 134 | 135 | [OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式](https://github.com/FreeBlues/ShaderDebugger/blob/master/Record-Article-1-cn.md) 136 | [OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型](https://github.com/FreeBlues/ShaderDebugger/blob/master/Record-Article-2-cn.md) 137 | -------------------------------------------------------------------------------- /Record-Article-1-cn.md: -------------------------------------------------------------------------------- 1 | # OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式 2 | 3 | ## --是什么(答案是具体值) VS 是不是(答案是布尔值) 4 | 5 | # 目录 6 | 7 | ## 背景介绍 8 | 9 | ### 问题描述 10 | 11 | `Codea` 是 `iPad` 上的一款很方便的开发软件, 尤其是它支持 `OpenGL ES 2.0/3.0`, 支持着色器 `shader`, 可以直接写代码操纵 `GPU`. 不过也有不太方便的地方, 那就是在 `Codea` 上写 `OpenGL ES 2.0 Shader` 代码的时候发现跟踪 `shader` 内部使用的变量特别困难, 因为 `GPU` 就像一个黑洞, 程序员可以通过程序向 `vertex shader` 和 `fragment shader` 传递数据进去, 但是却没办法把 `shader` 的变量值传回来, 这样就导致在调试 `shader` 代码时看不到内部的变化, 有时候出了问题就得左右推测, 以往 `打印/输出` 变量值的调试方法也失效了, 结果使得调试 `shader` 代码比较困难. 12 | 13 | ### 已知条件 14 | 15 | 但是 `shader` 还是要输出信息的, 只不过它输出的信息是 `gl_Position` 和 `gl_FragColor`, 前者是一个四维向量用于设定屏幕图像像素点坐标, 后者也是一个四维向量用于设定颜色值, 而这两个信息是无法直接为我们输出变量值的. 那么是否可以做一点文章, 通过一种间接的方式来传递我们需要知道的信息呢? 16 | 17 | 18 | ## 解决思路 19 | 20 | ### 转换思维 21 | 22 | 昨天晚上, 在调试一段简单但是比较有趣的 `shader` 代码时, 忽然产生了一个灵感:为什么不改变一下对 `shader` 提问的方式? 我们之前在调试普通程序时使用的 `打印/输出` 技巧实际上等价于向计算机提出一个问题: 请告诉我那个变量的值是多少? 很显然, `shader` 程序没有办法直接告诉我们那个变量是多少, 那么换一个思维, 改成问计算机: 请告诉我那个变量的值是不是大于100? 这下就简单了, 因为 `shader` 是很容易回答这个问题的, 因为这个问题的答案要么是 `true`, 要么是 `false`. 23 | 24 | 25 | ### 第一种简单方案 26 | 27 | 我们很容易设计一段 `shader` 绘图代码, 如果答案是 `true`, 那么我们在指定区域挥着红色, 如果答案是 `false`, 那么我们在指定区域绘制绿色, 这样 `GPU` 就可以通过屏幕间接地把我们需要的信息传递出来了. 28 | 29 | 30 | 假设要观察的变量为 `myVar`, 想要观察它是否大于100, `shader`实现代码如下: 31 | 32 | 33 | ``` 34 | //这段代码放在 fragment shader 的 main 函数的最后面 35 | void main() 36 | 37 | ... 38 | 39 | // 取得坐标 40 | float x = vTexCoord.x; 41 | float y = vTexCoord.y; 42 | 43 | // 设定调试区显示范围为右上角 44 | if(x > 0.9 && y > 0.9) { 45 | if(myVar > 100){ 46 | // 答案为 true 则设置调试区颜色为红色 47 | gl_FragColor = vec4(1,0,0,.5); 48 | }else{ 49 | // 答案为 false 则设置调试区颜色为红色 50 | gl_FragColor = vec4(1,0,0,.5); 51 | } 52 | } 53 | } 54 | ``` 55 | 56 | 完整的 `shader` 代码为: 57 | 58 | ``` 59 | myShader = { 60 | vsBase = [[ 61 | // vertex shader 代码 62 | uniform mat4 modelViewProjection; 63 | uniform vec2 uResolution; 64 | 65 | attribute vec4 position; 66 | attribute vec4 color; 67 | attribute vec2 texCoord; 68 | 69 | varying lowp vec4 vColor; 70 | varying highp vec2 vTexCoord; 71 | 72 | void main() { 73 | vColor=color; 74 | vTexCoord = texCoord; 75 | 76 | gl_Position = modelViewProjection * position; 77 | } 78 | ]], 79 | fsBase = [[ 80 | // fragment shader 代码 81 | precision highp float; 82 | uniform lowp sampler2D texture; 83 | varying lowp vec4 vColor; 84 | varying highp vec2 vTexCoord; 85 | 86 | void main() { 87 | lowp vec4 col = texture2D( texture, vTexCoord ) * vColor; 88 | 89 | // 默认全部设置为白色 90 | gl_FragColor = vec4(1,1,1,1); 91 | 92 | // 测试变量 myVar, 可分别设置为 >100 和 <=100 两个区间的值 93 | int myVar = 1; 94 | 95 | // 取得坐标 96 | float x = vTexCoord.x; 97 | float y = vTexCoord.y; 98 | 99 | // 设定调试区显示范围为右上角 100 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 101 | if(myVar > 100){ 102 | // 答案为 true 则设置调试区颜色为红色 103 | gl_FragColor = vec4(1,0,0,1); 104 | }else { 105 | // 答案为 false 则设置调试区颜色为红色 106 | gl_FragColor = vec4(0,1,0,1); 107 | } 108 | } 109 | } 110 | ]] 111 | } 112 | ``` 113 | 114 | 配套的 `Codea` 代码为: 115 | 116 | 117 | ``` 118 | -- Shader debug 119 | displayMode(OVERLAY) 120 | function setup() 121 | m = mesh() 122 | 123 | -- 首先得有顶点,后续要把顶点数据传给 vertex Shader 处理程序 124 | m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) 125 | 126 | -- 设置 shader 127 | m.shader = shader(myShader.vsBase,myShader.fsBase) 128 | 129 | -- 要有 texture 才能设置颜色 130 | -- m.texture = "Documents:univer" 131 | m:setColors(color(220,200,200,255)) 132 | 133 | -- 观察 134 | parameter.watch("m.shader.modelViewProjection") 135 | parameter.watch("m.shader.uResolution") 136 | parameter.watch("m.vertices[1]") 137 | 138 | end 139 | 140 | function draw() 141 | background(0) 142 | m:draw() 143 | end 144 | 145 | function touched(touch) 146 | 147 | end 148 | 149 | ``` 150 | 151 | 152 | 没有`Codea`的用户可以在`XCode`下编译该项目, 然后在模拟器查看执行结果, `XCode` 项目文件下载地址[XCode项目文件链接](http://pan.baidu.com/s/1miISuVI): 153 | 154 | 155 | 运行截图如下: 156 | 157 | `myVar` 大于 100 158 | ![红色](https://static.oschina.net/uploads/img/201603/31014036_Tzbf.png "在这里输入图片标题") 159 | 160 | `myVar` 小于等于 100 161 | ![绿色](https://static.oschina.net/uploads/img/201603/31014119_ZqAW.png "在这里输入图片标题") 162 | 163 | 164 | ### 可以显示变量值的方案 165 | 166 | 非常好, 通过上面的试验, 我们终于可以大致了解 `shader` 中变量的情况了, 比如说它是不是大于某个数, 是不是小于某个数, 是不是正数, 是不是负数, 等等. 但是这种调试信息还是太粗糙, 而且用起来也比较繁琐. 那么更进一步, 我们还是希望能看到变量的具体的值, 前面说过 `shader` 没办法像 `printf` 一样, 直接把变量值打印到屏幕, 但是我们知道我们实际上可以通过 `shader` 完全控制屏幕输出, 所以理论上我们可以在屏幕上绘制出任何内容, 包括数字. 167 | 168 | 169 | 现在先简化一下问题, 假设 `myVar` 的取值范围是整数 `0~9`, 那么我们可以设计一种对应算法, 处理逻辑是这样的: 170 | 171 | ``` 172 | 如果 myVar 是 1, 那么我们在指定的区域内把指定的像素点用指定的颜色绘制(绘制出1); 173 | 如果 myVar 是 2, 那么我们在指定的区域内把指定的像素点用指定的颜色绘制(绘制出2); 174 | ... 175 | 如果 myVar 是 9, 那么我们在指定的区域内把指定的像素点用指定的颜色绘制(绘制出9); 176 | 如果 myVar 是 0, 那么我们在指定的区域内把指定的像素点用指定的颜色绘制(绘制出0); 177 | ``` 178 | 179 | 听起来不错, 这样我们就可以让 `shader` 输出 `1~0` 10个数字了, 继续简化问题, 先从最简单的地方入手, 我们试着处理一下 `1`, 暂时不管 `myVar` 的值, 我们只是简单地在屏幕上绘制一个 `1`, 那么代码如下: 180 | 181 | ``` 182 | // 取得坐标 183 | float x = vTexCoord.x; 184 | float y = vTexCoord.y; 185 | 186 | // 先设置好调试区的范围 187 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 188 | // 设置正方形区域的颜色为白色作为背景色 189 | gl_FragColor = vec4(1,1,1,1); 190 | // 相当于在一个正方形内绘制一个 `1`, 我们选择最右边 191 | if( x > 0.99 ){ 192 | // 最右边设置为绿色 193 | gl_FragColor = vec4(0,1,0,1); 194 | } 195 | } 196 | ``` 197 | 截图如下: 198 | 199 | 200 | 很好, 继续, 增加一个判断条件 `myVar是否为1`, 否则只要执行到这个区域坐标一律会绘制一个白底绿色的数字`1`, 如下: 201 | 202 | ``` 203 | // 取得坐标 204 | float x = vTexCoord.x; 205 | float y = vTexCoord.y; 206 | 207 | // 先设置好调试区的范围 208 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 209 | // 设置正方形区域的颜色为白色作为背景色 210 | gl_FragColor = vec4(1,1,1,1); 211 | // 相当于在一个正方形内绘制一个 `1`, 我们选择最右边 212 | if( myVar == 1 && x > 0.99 ){ 213 | // 最右边设置为绿色 214 | gl_FragColor = vec4(0,1,0,1); 215 | } 216 | } 217 | ``` 218 | 219 | 接着我们可以把 `2~0` 的数字全部以这种方式绘制出来, 为了简单起见, 数字造型全部采用类似7段数码管的那种风格, 伪码如下: 220 | 221 | ``` 222 | // 取得坐标 223 | float x = vTexCoord.x; 224 | float y = vTexCoord.y; 225 | 226 | // 先设置好调试区的范围 227 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 228 | // 设置正方形区域的颜色为白色作为背景色 229 | gl_FragColor = vec4(1,1,1,1); 230 | // 相当于在一个正方形内绘制一个 `1`, 我们选择最右边 231 | if( myVar == 1 && x > 0.99 ){ 232 | // 最右边设置为绿色 233 | gl_FragColor = vec4(0,1,0,1); 234 | } 235 | if( myVar == 2 && (2的绘制坐标范围) ){ 236 | // 最右边设置为绿色 237 | gl_FragColor = vec4(0,1,0,1); 238 | } 239 | ... 240 | if( myVar == 0 && (0的坐标绘制范围) ){ 241 | // 最右边设置为绿色 242 | gl_FragColor = vec4(0,1,0,1); 243 | } 244 | } 245 | ``` 246 | 247 | 回头看看代码, 发现貌似有很多重复的地方, 稍微合并一下, 伪码如下: 248 | 249 | ``` 250 | // 取得坐标 251 | float x = vTexCoord.x; 252 | float y = vTexCoord.y; 253 | 254 | // 先设置好调试区的范围 255 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 256 | // 设置正方形区域的颜色为白色作为背景色 257 | gl_FragColor = vec4(1,1,1,1); 258 | // 相当于在一个正方形内绘制一个 `1`, 我们选择最右边 259 | if(( myVar == 1 && x > 0.99 ) || 260 | ( myVar == 2 && (2的绘制坐标范围)) || 261 | ... 262 | ( myVar == 0 && (0的坐标绘制范围)) 263 | ) 264 | { 265 | // 最右边设置为绿色 266 | gl_FragColor = vec4(0,1,0,1); 267 | } 268 | } 269 | ``` 270 | 271 | 基本上就是这样样子, 把它写成函数形式, 代码如下: 272 | 273 | ``` 274 | // 构造数字 275 | void ledChar(int n, float xa,float xb, float ya, float yb){ 276 | float x = vTexCoord.x; 277 | float y = vTexCoord.y; 278 | float x1 = xa; 279 | float x2 = xa+xb; 280 | float y1 = ya; 281 | float y2 = ya+yb; 282 | float ox = (x2+x1)/2.0; 283 | float oy = (y2+y1)/2.0; 284 | float dx = (x2-x1)/10.0; 285 | float dy = (y2-y1)/10.0; 286 | float b = (x2-x1)/20.0; 287 | int num = n; 288 | 289 | // 设定调试区显示范围 290 | if(x >= x1 && x <= x2 && y >= y1 && y <= y2) { 291 | // 设置调试区背景色的半透明的蓝色 292 | gl_FragColor = vec4(0,0,1,.5); 293 | // 分别绘制出 LED 形式的数字 1~0 294 | if((num==1 && (x > x2-dx)) || 295 | (num==2 && ((y > y2-dy) || (x > x2-dx && y > oy-dy/2.0) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (x < x1+dx && y < oy+dy/2.0) || (y < y1+dy))) || 296 | (num==3 && ((y > y2-dy) || (x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (y < y1+dy))) || 297 | (num==4 && ((x < x1+dx && y > oy-dy/2.0) ||(x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0))) || 298 | (num==5 && ((y > y2-dy) || (x < x1+dx && y > oy-dy/2.0)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x > x2-dx))) || 301 | (num==8 && ((y > y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx) || (y y2-dy) || (x < x1+dx && y > oy-dy/2.0)||(y > oy-dy/2.0 && y < oy+dy/2.0)|| (x>x2-dx) || (y y2-dy) || (x < x1+dx) || (x>x2-dx) || (y= x1 && x <= x2 && y >= y1 && y <= y2) { 372 | // 设置调试区背景色为半透明的蓝色 373 | gl_FragColor = vec4(0.2,0.8,0.2,.5); 374 | // 分别绘制出 LED 形式的数字 1~0 375 | if((num==1 && (x > x2-dx)) || 376 | (num==2 && ((y > y2-dy) || (x > x2-dx && y > oy-dy/2.0) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (x < x1+dx && y < oy+dy/2.0) || (y < y1+dy))) || 377 | (num==3 && ((y > y2-dy) || (x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (y < y1+dy))) || 378 | (num==4 && ((x < x1+dx && y > oy-dy/2.0) ||(x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0))) || 379 | (num==5 && ((y > y2-dy) || (x < x1+dx && y > oy-dy/2.0)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x > x2-dx))) || 382 | (num==8 && ((y > y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx) || (y y2-dy) || (x < x1+dx && y > oy-dy/2.0)||(y > oy-dy/2.0 && y < oy+dy/2.0)|| (x>x2-dx) || (y y2-dy) || (x < x1+dx) || (x>x2-dx) || (y 0.9 && y > 0.9) { 34 | if(myVar > 100){ 35 | // true is red 36 | gl_FragColor = vec4(1,0,0,.5); 37 | }else{ 38 | // false is green 39 | gl_FragColor = vec4(0,1,0,.5); 40 | } 41 | } 42 | } 43 | ``` 44 | 45 | The whole `shader` code is below: 46 | 47 | ``` 48 | myShader = { 49 | vsBase = [[ 50 | // vertex shader 51 | uniform mat4 modelViewProjection; 52 | uniform vec2 uResolution; 53 | 54 | attribute vec4 position; 55 | attribute vec4 color; 56 | attribute vec2 texCoord; 57 | 58 | varying lowp vec4 vColor; 59 | varying highp vec2 vTexCoord; 60 | 61 | void main() { 62 | vColor=color; 63 | vTexCoord = texCoord; 64 | 65 | gl_Position = modelViewProjection * position; 66 | } 67 | ]], 68 | fsBase = [[ 69 | // fragment shader 70 | precision highp float; 71 | uniform lowp sampler2D texture; 72 | varying lowp vec4 vColor; 73 | varying highp vec2 vTexCoord; 74 | 75 | void main() { 76 | lowp vec4 col = texture2D( texture, vTexCoord ) * vColor; 77 | 78 | // default is white 79 | gl_FragColor = vec4(1,1,1,1); 80 | 81 | // test var 82 | int myVar = 1; 83 | 84 | float x = vTexCoord.x; 85 | float y = vTexCoord.y; 86 | 87 | // debug area 88 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 89 | if(myVar > 100){ 90 | // true is red 91 | gl_FragColor = vec4(1,0,0,1); 92 | }else { 93 | // false is green 94 | gl_FragColor = vec4(0,1,0,1); 95 | } 96 | } 97 | } 98 | ]] 99 | } 100 | ``` 101 | 102 | The `Codea` code is: 103 | 104 | ``` 105 | -- Shader debug 106 | displayMode(OVERLAY) 107 | function setup() 108 | m = mesh() 109 | m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) 110 | m.shader = shader(myShader.vsBase,myShader.fsBase) 111 | 112 | -- m.texture = "Documents:univer" 113 | m:setColors(color(220,200,200,255)) 114 | 115 | parameter.watch("m.shader.modelViewProjection") 116 | parameter.watch("m.shader.uResolution") 117 | parameter.watch("m.vertices[1]") 118 | 119 | end 120 | 121 | function draw() 122 | background(0) 123 | m:draw() 124 | end 125 | 126 | function touched(touch) 127 | 128 | end 129 | 130 | ``` 131 | 132 | ### The 2nd solution: Show the value 133 | 134 | Very well, with the experiment above, we can know the brief of the var in `shader`. But the info is too simple, and it is complicated to use. We want to watch the exact value, we know we can control the whole screen with `shader`, so in abstracto we can draw anything to screen include number. 135 | 136 | 137 | Now let us make the problem simple, assume `myVar` is a int and its region is [0,9], then we can get a logic: 138 | 139 | 140 | ``` 141 | if myVar is 1, then draw specified pixels in a specified area with specified color(draw 1); 142 | if myVar is 2, then draw specified pixels in a specified area with specified color(draw 2); 143 | ... 144 | if myVar is 9, then draw specified pixels in a specified area with specified color(draw 9); 145 | if myVar is 0, then draw specified pixels in a specified area with specified color(draw 0); 146 | ``` 147 | 148 | 149 | It looks good, now we can make `shader` output the 10 number of `1~0`. Continue to make the problem simpler, start with the easiest place, only to draw a number `1` on the screen, the code is below: 150 | 151 | ``` 152 | float x = vTexCoord.x; 153 | float y = vTexCoord.y; 154 | 155 | // debug area 156 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 157 | // background is white 158 | gl_FragColor = vec4(1,1,1,1); 159 | // draw 1 in a rect 160 | if( x > 0.99 ){ 161 | // the right set green 162 | gl_FragColor = vec4(0,1,0,1); 163 | } 164 | } 165 | ``` 166 | 167 | 168 | Ok, it works, now we can add more, for example we can add a judgement with `if myVar is number 1`, if it is `true` the `shader` will draw a green number 1 with white background in this area. 169 | 170 | ``` 171 | float x = vTexCoord.x; 172 | float y = vTexCoord.y; 173 | 174 | // debug area 175 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 176 | // background is white 177 | gl_FragColor = vec4(1,1,1,1); 178 | // draw 1 in a rect 179 | if( myVar == 1 && x > 0.99 ){ 180 | // set green 181 | gl_FragColor = vec4(0,1,0,1); 182 | } 183 | } 184 | ``` 185 | 186 | One by one, we can draw the number `2~0` with the same method: 187 | 188 | ``` 189 | float x = vTexCoord.x; 190 | float y = vTexCoord.y; 191 | 192 | // debug area 193 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 194 | // background is white 195 | gl_FragColor = vec4(1,1,1,1); 196 | // draw 1 in a rect 197 | if( myVar == 1 && x > 0.99 ){ 198 | // set green 199 | gl_FragColor = vec4(0,1,0,1); 200 | } 201 | if( myVar == 2 && (the coordinate of 2) ){ 202 | // set green 203 | gl_FragColor = vec4(0,1,0,1); 204 | } 205 | ... 206 | if( myVar == 0 && (the coordinate of 0) ){ 207 | // set green 208 | gl_FragColor = vec4(0,1,0,1); 209 | } 210 | } 211 | ``` 212 | 213 | 214 | Optimize the code: 215 | 216 | ``` 217 | float x = vTexCoord.x; 218 | float y = vTexCoord.y; 219 | 220 | // debug area 221 | if(x > 0.9 && x < 1.0 && y > 0.9 && y < 1.0) { 222 | // background is white 223 | gl_FragColor = vec4(1,1,1,1); 224 | // draw 1 in a rect 225 | if(( myVar == 1 && x > 0.99 ) || 226 | ( myVar == 2 && (the coordinate of 2)) || 227 | ... 228 | ( myVar == 0 && (the coordinate of 0)) 229 | ) 230 | { 231 | // set green 232 | gl_FragColor = vec4(0,1,0,1); 233 | } 234 | } 235 | ``` 236 | 237 | 238 | It is ok, change it to a function: 239 | 240 | ``` 241 | // LED char 242 | void ledChar(int n, float xa,float xb, float ya, float yb){ 243 | float x = vTexCoord.x; 244 | float y = vTexCoord.y; 245 | float x1 = xa; 246 | float x2 = xa+xb; 247 | float y1 = ya; 248 | float y2 = ya+yb; 249 | float ox = (x2+x1)/2.0; 250 | float oy = (y2+y1)/2.0; 251 | float dx = (x2-x1)/10.0; 252 | float dy = (y2-y1)/10.0; 253 | float b = (x2-x1)/20.0; 254 | int num = n; 255 | 256 | // debug area 257 | if(x >= x1 && x <= x2 && y >= y1 && y <= y2) { 258 | // background is white 259 | gl_FragColor = vec4(0,0,1,.5); 260 | // draw 1~0 in a rect 261 | if((num==1 && (x > x2-dx)) || 262 | (num==2 && ((y > y2-dy) || (x > x2-dx && y > oy-dy/2.0) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (x < x1+dx && y < oy+dy/2.0) || (y < y1+dy))) || 263 | (num==3 && ((y > y2-dy) || (x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (y < y1+dy))) || 264 | (num==4 && ((x < x1+dx && y > oy-dy/2.0) ||(x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0))) || 265 | (num==5 && ((y > y2-dy) || (x < x1+dx && y > oy-dy/2.0)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x > x2-dx))) || 268 | (num==8 && ((y > y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx) || (y y2-dy) || (x < x1+dx && y > oy-dy/2.0)||(y > oy-dy/2.0 && y < oy+dy/2.0)|| (x>x2-dx) || (y y2-dy) || (x < x1+dx) || (x>x2-dx) || (y= x1 && x <= x2 && y >= y1 && y <= y2) { 339 | // set blue 340 | gl_FragColor = vec4(0.2,0.8,0.2,.5); 341 | // draw number 1~0 342 | if((num==1 && (x > x2-dx)) || 343 | (num==2 && ((y > y2-dy) || (x > x2-dx && y > oy-dy/2.0) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (x < x1+dx && y < oy+dy/2.0) || (y < y1+dy))) || 344 | (num==3 && ((y > y2-dy) || (x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (y < y1+dy))) || 345 | (num==4 && ((x < x1+dx && y > oy-dy/2.0) ||(x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0))) || 346 | (num==5 && ((y > y2-dy) || (x < x1+dx && y > oy-dy/2.0)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x > x2-dx))) || 349 | (num==8 && ((y > y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx) || (y y2-dy) || (x < x1+dx && y > oy-dy/2.0)||(y > oy-dy/2.0 && y < oy+dy/2.0)|| (x>x2-dx) || (y y2-dy) || (x < x1+dx) || (x>x2-dx) || (y= x1 && x <= x2 && y >= y1 && y <= y2) { 34 | // 设置调试区背景色 35 | gl_FragColor = vec4(0.2,0.2,0.8,.5); 36 | // 分别绘制出 LED 形式的数字 1~0 37 | if((num==1 && (x > x2-dx)) || 38 | (num==2 && ((y > y2-dy) || (x > x2-dx && y > oy-dy/2.0) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (x < x1+dx && y < oy+dy/2.0) || (y < y1+dy))) || 39 | (num==3 && ((y > y2-dy) || (x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0) || (y < y1+dy))) || 40 | (num==4 && ((x < x1+dx && y > oy-dy/2.0) ||(x > x2-dx) || (y > oy-dy/2.0 && y < oy+dy/2.0))) || 41 | (num==5 && ((y > y2-dy) || (x < x1+dx && y > oy-dy/2.0)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx && y y2-dy) || (x > x2-dx))) || 44 | (num==8 && ((y > y2-dy) || (x < x1+dx)|| (y > oy-dy/2.0 && y < oy+dy/2.0) || (x>x2-dx) || (y y2-dy) || (x < x1+dx && y > oy-dy/2.0)||(y > oy-dy/2.0 && y < oy+dy/2.0)|| (x>x2-dx) || (y y2-dy) || (x < x1+dx) || (x>x2-dx) || (yx1 && xy1 && y= x1 && x <= x2 && y >= y1 && y <= y2) { 90 | // 设置调试区背景色为绿色 91 | gl_FragColor = vec4(0.2,1.0,0.2,1.0); 92 | // 分别绘制出 LED 形式的数字 1~0 , 用黑色绘制1个或2个矩形,由矩形以外的绿色区域组成字型 93 | if((num==1 && (inRect(x1,ox-dx,y1,y2) || inRect(ox+dx,x2,y1,y2))) || 94 | (num==2 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2,y1+dy,oy-dy/2.0))) || 95 | (num==3 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 96 | (num==4 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2) || inRect(x1,x2-dx,y1,oy-dy/2.0))) || 97 | (num==5 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 98 | (num==6 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy))) || 99 | (num==7 && inRect(x1,x2-dx,y1,y2-dy)) || 100 | (num==8 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy/2.0))) || 101 | (num==9 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 102 | (num==0 && inRect(x1+dx,x2-dx,y1+dy,y2-dy)) 103 | ) 104 | { 105 | gl_FragColor = vec4(0,0,0,.5); 106 | } 107 | } 108 | } 109 | ``` 110 | 111 | 这样考虑角度一变, 就发现其实用矩形在这种场景下更简单, 而且代码看起来清楚多了, 数字 `7` 和 `0` 用了一个矩形, 其他数字都用两个矩形就"掩"出来了(其实`1`用1个矩形就可以, 用两个是为了更美观一些) 112 | . 113 | 114 | 另外, 貌似使用了太多的函数, 导致效率不高, 其实我也很乐意把这些函数全部都写成宏, 只是不太会写带参数的宏, 试了半天 `inRect`, 比如 115 | 116 | ``` 117 | // ! 说明, 这是错的, 编译不通过 118 | #define inRect(x1,x2,y1,y2) x>(x1)&&x<(x2)&&y>(y1)&&y<(y2)?true:false 119 | ``` 120 | 121 | 结果老是有错误, 就没继续研究了. 122 | 123 | ## 改进为可用的原型 124 | 125 | 用了改进版的 `ledRectChar` 作为基础函数, 我们开始考虑实际的使用场景, 实际编程过程中 `shader` 用到的变量的值肯定不会只是一个一位整数, 所以我们首先得考虑多位整数, 其次还要考虑浮点数, 另外还要考虑负数的表示, 最后要考虑的是表示范围和准确度(这点最麻烦, 本文只是大致说一下). 126 | 127 | 列一下后续的需求清单: 128 | 129 | * 多位正整数 130 | * 多位浮点数 131 | * 负数 132 | * 给出表示范围和准确度 133 | 134 | 接下来我们一项一项来 135 | 136 | ### 表示多位正整数 137 | 138 | 在我发出前文 [OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式](http://my.oschina.net/freeblues/blog/650307) 后, 论坛上的一位朋友 `@dave1707` 用我们的基础函数 `ledChar` 写了一段表示多位正整数的代码, 并建议我把它完善一下, 首先表示感谢, 他的代码如下: 139 | 140 | ``` 141 | highp int nbr=8293; // number to display 142 | 143 | float m=0.96; 144 | while (nbr>0) 145 | { m=m-0.015; 146 | int nn=nbr-((nbr/10)*10); 147 | ledChar(nn, m, 0.01, 0.96, 0.01); 148 | nbr=nbr/10; 149 | } 150 | ``` 151 | 也就是说多位正整数的需求已经解决, 这里存在一个存储精度的问题, 也就是当要表示的数字大于某个值时就会导致溢出, 这种情况我们不做太多处理, 主要是因为这里处理起来比较麻烦, 我们会在注释中说明本函数适用的数字范围. 152 | 153 | 接下来我们会在他的代码的基础上继续前进, 我们先分析一下 `多位浮点数` 和 `负数` 这两个需求, 发现它们一个需要 `小数点`, 一个需要`负号`, 也就是说在我们的基础函数 `ledRectChar` 中需要新增两种字型, 那么我们先来升级一下基础函数. 154 | 155 | ### 新增的两种字型:小数点和负号 156 | 157 | 先来处理`小数点`, 前面用变量 `num` 的值 `1~0` 分别表示 `1~0` 这10个数字的字型, 那么新增的`小数点`和负号分别用数字 `10` 和 `11` 表示, 然后用"矩形掩码"把它们的字型画出. 158 | 159 | 增加这么两行判断: 160 | 161 | ``` 162 | (num==10 && (inRect(x1,x2,oy-dy,y2) || inRect(x1,ox-dx*2.0,y1,oy-dy) || inRect(ox+dx*2.0,x2,y1,oy-dy) )) || 163 | (num==11 && (inRect(x1,x2,oy+dy,y2) || inRect(x1,x2,y1,oy-dy))) 164 | ``` 165 | 166 | 更新后的 `ledRectChar` 函数如下: 167 | 168 | ``` 169 | void ledRectChar(int n, float xa,float xb, float ya, float yb){ 170 | float x1 = xa; 171 | float x2 = xa+xb; 172 | float y1 = ya; 173 | float y2 = ya+yb; 174 | float ox = (x2+x1)/2.0; 175 | float oy = (y2+y1)/2.0; 176 | float dx = (x2-x1)/10.0; 177 | float dy = (y2-y1)/10.0; 178 | float b = (x2-x1)/20.0; 179 | int num = n; 180 | 181 | // 设定调试区显示范围 182 | if(x >= x1 && x <= x2 && y >= y1 && y <= y2) { 183 | // 设置调试区背景色 184 | gl_FragColor = vec4(0.2,1.0,0.2,1.0); 185 | // 分别绘制出 LED 形式的数字 1~0 , 用黑色绘制1个或2个矩形,由矩形以外的绿色区域组成字型 186 | if((num==1 && (inRect(x1,ox-dx,y1,y2) || inRect(ox+dx,x2,y1,y2))) || 187 | (num==2 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2,y1+dy,oy-dy/2.0))) || 188 | (num==3 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 189 | (num==4 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2) || inRect(x1,x2-dx,y1,oy-dy/2.0))) || 190 | (num==5 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 191 | (num==6 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy))) || 192 | (num==7 && inRect(x1,x2-dx,y1,y2-dy)) || 193 | (num==8 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy/2.0))) || 194 | (num==9 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 195 | (num==0 && inRect(x1+dx,x2-dx,y1+dy,y2-dy)) || 196 | // 传入10则绘制小数点, 传入11则绘制负号, 传入12则清空 197 | (num==10 && (inRect(x1,x2,oy-dy,y2) || inRect(x1,ox-dx*2.0,y1,oy-dy) || inRect(ox+dx*2.0,x2,y1,oy-dy) )) || 198 | (num==11 && (inRect(x1,x2,oy+dy,y2) || inRect(x1,x2,y1,oy-dy))) || 199 | (num==12) 200 | ) 201 | { 202 | gl_FragColor = vec4(0,0,0,.5); 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | ### 浮点数和负数 209 | 210 | 调试通过, OK, 现在基础函数已经可以提供全部字型了, 我们接着来实现浮点数的表示, 基于前面 `@dave1707` 的代码, 把浮点数先取绝对值(为了避免要区分正负数分别对应的`floor`和`ceil`两种取整方式), 再把绝对值分离出整数部分和小数部分, 然后都当做整数来处理, 按`个十百千万...`位插入一个20位的数组, 整数部分和小数部分中间插一个`小数点`所对应的数字`10`, 最后判断一下是不是负数, 是的话就插入`负号`所对应的数字`11`, 还有就是开始要初始化一下数组, 初始值置为 `12`(在基础函数中对应黑色背景), 否则数组默认值都是 `0`, 显示时会在空白的数字位全部显示为 `0`, 影响观感, 代码如下: 211 | 212 | ``` 213 | void showFloat(float f){ 214 | int myNum[20]; 215 | int k = 0; 216 | int iPart = int(floor(abs(f))); 217 | int fPart = int(fract(abs(f))*100000.0); 218 | float m=0.86; 219 | 220 | // 初始化数组,全部置为代表黑色的12 221 | for(int i=0; i<20; i++){ 222 | myNum[i] = 12; 223 | } 224 | 225 | // 插入小数部分 226 | while (fPart>0) 227 | { 228 | // 从个位开始, 依次取出个位,十位,百位,千位...的数字值 229 | myNum[k++]=fPart-((fPart/10)*10); 230 | fPart=fPart/10; 231 | } 232 | 233 | // 如果是0 234 | if(f==0.0){myNum[k++] = 0;} 235 | 236 | // 插入小数点 237 | myNum[k++] = 10; 238 | 239 | // 插入整数部分 240 | while (iPart>0) 241 | { 242 | myNum[k++]=iPart-((iPart/10)*10); 243 | iPart=iPart/10; 244 | } 245 | 246 | // 如果是负数,则插入代表负号的11 247 | if(f<0.0) { myNum[k++]=11;} 248 | 249 | // 循环输出数字数组 250 | for(int i=0; i<20; i++) 251 | { 252 | m = m-0.03; 253 | ledRectChar(myNum[i], m, 0.02, 0.6, 0.15); 254 | } 255 | 256 | } 257 | ``` 258 | 259 | 很好, 调试通过, 基本搞定, 好像忘记处理负整数了, 为了避免麻烦, 我们可以建议用户把`负整数`进行强制类型转换为`负浮点数`, 就可以直接使用我们的 `showFloat` 函数了, 具体来说就是这么调用: 260 | 261 | 262 | * showFloat(float(-1234)); 263 | 264 | 265 | 显示截图如下: 266 | ![-1234](https://static.oschina.net/uploads/img/201604/02012716_WSKI.png "在这里输入图片标题") 267 | 268 | 269 | ### 显示范围和准确度 270 | 271 | 最后说一下这个不得不说的问题, 很多编程语言都需要考虑一个数值表示范围, 尤其是浮点数, 比如 `shader` 里的浮点数就是数值越大, 小数位越少, 而且这时比较小的小数会被舍掉, 我们为小数部分留了5位, 整数部分留了13位, 当然, 如果你需要调试更大的数, 也可以自己修改数组的大小--不过好像 `shader` 中太大的数会返回溢出, 大家根据自己的需求看着办吧. 272 | 273 | 看看这几个截图: 274 | 275 | * showFloat(2097152.411); 276 | 277 | ![舍掉小数](https://static.oschina.net/uploads/img/201604/02012812_2yQ2.png "在这里输入图片标题") 278 | 279 | * showFloat(2097152.11); 280 | 281 | ![保留小数](https://static.oschina.net/uploads/img/201604/02012855_bQRE.png "在这里输入图片标题") 282 | 283 | 284 | 在此不得不赞叹一下我大 `Common Lisp` 的强悍, 毕竟能直接计算 `1024^1024` (1024的1024次方)的语言唯有我大 `Common Lisp` 了, 看看: 285 | 286 | 截图: 287 | ![大数字](https://static.oschina.net/uploads/img/201604/02012942_94EU.png "在这里输入图片标题") 288 | 289 | 290 | ## 可用原型的完整代码 291 | 292 | 现在我们基本完成一个可用原型了, 虽然效率不怎么样, `FPS`甚至降低了20倍(从60降低到3), 但是首先我们解决了有没有的问题, 好不好的问题就留待后面解决了, 如果有需求那就继续优化好了, 下面给出可用原型的全部代码: 293 | 294 | ### shader代码 295 | 296 | ``` 297 | myShader = { 298 | vsBase = [[ 299 | // vertex shader 代码 300 | uniform mat4 modelViewProjection; 301 | uniform vec2 uResolution; 302 | 303 | attribute vec4 position; 304 | attribute vec4 color; 305 | attribute vec2 texCoord; 306 | 307 | varying lowp vec4 vColor; 308 | varying highp vec2 vTexCoord; 309 | 310 | void main() { 311 | vColor=color; 312 | vTexCoord = texCoord; 313 | 314 | gl_Position = modelViewProjection * position; 315 | } 316 | ]], 317 | fsBase = [[ 318 | // fragment shader 代码 319 | precision highp float; 320 | uniform lowp sampler2D texture; 321 | varying lowp vec4 vColor; 322 | varying highp vec2 vTexCoord; 323 | 324 | float x = vTexCoord.x; 325 | float y = vTexCoord.y; 326 | 327 | void ledChar(int,float,float,float,float); 328 | void ledRectChar(int,float,float,float,float); 329 | void showInt(int); 330 | void showFloat(float); 331 | bool inRect(float,float,float,float); 332 | 333 | void main() { 334 | lowp vec4 col = texture2D( texture, vTexCoord ) * vColor; 335 | 336 | // 默认全部设置为黑色 337 | gl_FragColor = vec4(.1,.1,.1,1); 338 | 339 | showFloat(-.1111111); 340 | //showFloat(float(-9765)); 341 | 342 | } 343 | 344 | void showFloat(float f){ 345 | int myNum[20]; 346 | int k = 0; 347 | int iPart = int(floor(abs(f))); 348 | int fPart = int(fract(abs(f))*100000.0); 349 | float m=0.86; 350 | 351 | // 初始化数组,全部置为代表黑色的12 352 | for(int i=0; i<20; i++){ 353 | myNum[i] = 12; 354 | } 355 | 356 | // 插入小数部分 357 | while (fPart>0) 358 | { 359 | // 从个位开始, 依次取出个位,十位,百位,千位...的数字值 360 | myNum[k++]=fPart-((fPart/10)*10); 361 | fPart=fPart/10; 362 | } 363 | 364 | // 如果是0 365 | if(f==0.0){myNum[k++] = 0;} 366 | 367 | // 插入小数点 368 | myNum[k++] = 10; 369 | 370 | // 插入整数部分 371 | while (iPart>0) 372 | { 373 | myNum[k++]=iPart-((iPart/10)*10); 374 | iPart=iPart/10; 375 | } 376 | 377 | // 如果是负数,则插入代表负号的11 378 | if(f<0.0) { myNum[k++]=11;} 379 | 380 | // 循环输出数字数组 381 | for(int i=0; i<20; i++) 382 | { 383 | m = m-0.03; 384 | ledRectChar(myNum[i], m, 0.02, 0.6, 0.15); 385 | } 386 | } 387 | 388 | bool inRect(float x1,float x2, float y1, float y2){ 389 | if(x>x1 && xy1 && y= x1 && x <= x2 && y >= y1 && y <= y2) { 406 | // 设置调试区背景色 407 | gl_FragColor = vec4(0.2,1.0,0.2,1.0); 408 | // 分别绘制出 LED 形式的数字 1~0 , 用黑色绘制1个或2个矩形,由矩形以外的绿色区域组成字型 409 | if((num==1 && (inRect(x1,ox-dx,y1,y2) || inRect(ox+dx,x2,y1,y2))) || 410 | (num==2 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2,y1+dy,oy-dy/2.0))) || 411 | (num==3 && (inRect(x1,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 412 | (num==4 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2) || inRect(x1,x2-dx,y1,oy-dy/2.0))) || 413 | (num==5 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 414 | (num==6 && (inRect(x1+dx,x2,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy))) || 415 | (num==7 && inRect(x1,x2-dx,y1,y2-dy)) || 416 | (num==8 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1+dx,x2-dx,y1+dy,oy-dy/2.0))) || 417 | (num==9 && (inRect(x1+dx,x2-dx,oy+dy/2.0,y2-dy) || inRect(x1,x2-dx,y1+dy,oy-dy/2.0))) || 418 | (num==0 && inRect(x1+dx,x2-dx,y1+dy,y2-dy)) || 419 | // 传入10则绘制小数点, 传入11则绘制负号, 传入12则清空 420 | (num==10 && (inRect(x1,x2,oy-dy,y2) || inRect(x1,ox-dx*2.0,y1,oy-dy) || inRect(ox+dx*2.0,x2,y1,oy-dy) )) || 421 | (num==11 && (inRect(x1,x2,oy+dy,y2) || inRect(x1,x2,y1,oy-dy))) || 422 | (num==12) 423 | ) 424 | { 425 | gl_FragColor = vec4(0,0,0,.5); 426 | } 427 | } 428 | } 429 | 430 | ]] 431 | } 432 | ``` 433 | 434 | ### 配套Codea代码 435 | 436 | ``` 437 | -- Shader debug 438 | displayMode(OVERLAY) 439 | function setup() 440 | m = mesh() 441 | 442 | m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) 443 | 444 | m.shader = shader(myShader.vsBase,myShader.fsBase) 445 | 446 | -- m.texture = "Documents:univer" 447 | m:setColors(color(220,200,200,255)) 448 | 449 | parameter.watch("m.shader.modelViewProjection") 450 | parameter.watch("m.shader.uResolution") 451 | parameter.watch("m.vertices[1]") 452 | end 453 | 454 | function draw() 455 | background(0) 456 | m:draw() 457 | end 458 | 459 | function touched(touch) 460 | 461 | end 462 | ``` 463 | 464 | ## 后记 465 | 466 | 经过一番调试折腾, 终于完成一个刚刚能用的原型, 以后在 `Codea` 下调试 `shader` 程序起码有个工具勉强可用了. 当然, 这几个函数也可以用于调试其他平台的 `shader` 程序. 467 | 468 | 为了提高人类整体的工作效率, 我们后续会把这个原型发布到 `Github` 上, 以供其他需要观察 `shader` 内部变量的同学使用, 起个响亮的名字 `ShaderDebugger`: 469 | 470 | [Github 地址](https://github.com/FreeBlues/ShaderDebugger) 471 | [OSChina 地址](http://git.oschina.net/hexblues/ShaderDebugger) 472 | -------------------------------------------------------------------------------- /fragmentShader.c: -------------------------------------------------------------------------------- 1 | // fragment shader 代码 2 | precision highp float; 3 | uniform lowp sampler2D texture; 4 | varying lowp vec4 vColor; 5 | varying highp vec2 vTexCoord; 6 | 7 | bool inRect(float,float,float,float); 8 | void ledRectChar(int,float,float,float,float); 9 | void showFloat(float); 10 | 11 | void main() { 12 | lowp vec4 col = texture2D( texture, vTexCoord ) * vColor; 13 | 14 | // 默认全部设置为黑色 15 | gl_FragColor = vec4(.1,.1,.1,1); 16 | 17 | showFloat(-.1111111); 18 | //showFloat(float(-9765)); 19 | 20 | } 21 | 22 | void showFloat(float f){ 23 | int myNum[20]; 24 | int k = 0; 25 | int iPart = int(floor(abs(f))); 26 | int fPart = int(fract(abs(f))*100000.0); 27 | float m=0.86; 28 | 29 | // 初始化数组,全部置为代表黑色的12 30 | for(int i=0; i<20; i++){ 31 | myNum[i] = 12; 32 | } 33 | 34 | // 插入小数部分 35 | while (fPart>0) 36 | { 37 | // 从个位开始, 依次取出个位,十位,百位,千位...的数字值 38 | myNum[k++]=fPart-((fPart/10)*10); 39 | fPart=fPart/10; 40 | } 41 | 42 | // 如果是0 43 | if(f==0.0){myNum[k++] = 0;} 44 | 45 | // 插入小数点 46 | myNum[k++] = 10; 47 | 48 | // 插入整数部分 49 | while (iPart>0) 50 | { 51 | myNum[k++]=iPart-((iPart/10)*10); 52 | iPart=iPart/10; 53 | } 54 | 55 | // 如果是负数,则插入代表负号的11 56 | if(f<0.0) { myNum[k++]=11;} 57 | 58 | // 循环输出数字数组 59 | for(int i=0; i<20; i++) 60 | { 61 | m = m-0.03; 62 | ledRectChar(myNum[i], m, 0.02, 0.6, 0.15); 63 | } 64 | } 65 | 66 | bool inRect(float x1,float x2, float y1, float y2){ 67 | if(vTexCoord.x>x1 && vTexCoord.xy1 && vTexCoord.y