当前位置: 首页 > news >正文

Shadertoy 进阶 01

上一篇中简单阐述了下shader入门级hello world。

想了想,可以再深入的讲下我认识的 shadertoy 。

1. 坐标系

身为画家,最重要的当然是清楚自己的画布布局,专业一点,坐标系。shader中画图同样,需要我们首先了解坐标系,熟悉了坐标系才好在脑海中形成坐标系并进行构思几何。

除了听别人说的坐标系是什么,不如自己测试验证下记忆更深刻。这里我们写一段shader来测试下shadertoy的坐标系,代码如下,根据纹理坐标的范围进行了简单的逻辑判断,做出不同的颜色绘制。【这里请忽略性能问题,if else不适合gpu做并行计算 这里不重要哦>.<】

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = vec3(0,0,0);
    if(uv.x<0.5 && uv.y<0.5)
        col = vec3(1.0,0.0,0.0);
    else if(uv.x>0.5 && uv.y<0.5)
        col = vec3(0.0,1.0,0.0);
    else if(uv.x<0.5 &&uv.y>0.5)
        col = vec3(0.0,0.0,1.0);
    else if(uv.x>0.5 && uv.y>0.5)
        col = vec3(1.0,1.0,0.0);
    fragColor = vec4(col,1.0);
}

很明显,可以一眼看出来坐标系是左下角为(0.0,0.0),右上角为(1.0,1.0)。这样相信你就对自己的画布有了大概的认识了。

2、渲染一张纹理

再认识一个操作,渲染一张纹理到目标缓存区。操作见下图:

c8b2aa29321e7f01701c5ddc41dee9aa.png

修改着色器代码,来展示你选中的纹理

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = texture(iChannel0,uv).rgb;
    fragColor = vec4(col,1.0);
}

新引入的函数texture()采样函数,参数同glsl中一致,第一个参数为纹理id,第二个参数为纹理坐标

代码中写的是iChannel0,机智如你,相信你已经看到了,就是上图中“1.点击这里”指向位置的下标文本,如果想用更多的纹理,可以点击iChannel1,iChannel2,iChannel3,选择你要用的图,然后就这么方便。

3、示例体验

接下来,我打算列举几个简单的计算,理解一下shader中的绘图

eg1. 将一张图片横向绘制N次

线上效果图:

1cb9818cbe958ccd8f94b0619cafecca.png

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
 
    const float LineNum = 5.0;
    vec2 tmpUV = vec2(uv.x*LineNum,uv.y);
    vec2 realUV = vec2(tmpUV.x-floor(tmpUV.x),tmpUV.y);
    vec3 col = texture(iChannel0,realUV).rgb;
    
    fragColor = vec4(col,1.0);
}

shader逻辑:

uv:x,y[0.0,1.0]归一化纹理坐标

tmpUV:将x范围扩大到[0.0,5.0],y范围还是[0.0,1.0]

realUV: 将tmpUV的范围进一步处理,y照旧;x取值为当前值减去当前值向下取整剩余的浮点值,这个值范围刚好为[0.0,1.0]。因为texture函数的纹理坐标只能是[0.0,1.0]所以我们计算出这个值,对应的一采样就如效果图所示了。

eg2. 将一张图片弄成4宫格或者9宫格

ae66619fc39f56b91239373bdf996a20.png

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    
    const float hLineNum = 3.0;
    const float vLineNum = 3.0;
    
    vec2 tmpUV = vec2(uv.x*hLineNum,uv.y*vLineNum);
    vec2 realUV = vec2(tmpUV.x-floor(tmpUV.x),tmpUV.y-floor(tmpUV.y));
    vec3 col = texture(iChannel0,realUV).rgb;
    
    fragColor = vec4(col,1.0);
}

eg3. 将一张图片弄成的6宫格中个别区域弄上特殊处理

先上效果:[iChannel1是上面提到的选择了第二个纹理,图片任选]

e3a87f6f6ea07ecdc6599f0413fc52b7.png

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    
    float hLineNum = 2.0;
    float vLineNum = 3.0;
    
    vec2 tmpUV = vec2(uv.x*hLineNum,uv.y*vLineNum);
    vec2 realUV = vec2(tmpUV.x-floor(tmpUV.x),tmpUV.y-floor(tmpUV.y));
    vec3 col = texture(iChannel0,realUV).rgb;
    
    if(uv.x>0.5&&uv.y>0.5)
    {
        vec3 col2 = texture(iChannel1,realUV).rgb;
        col = col*col2;
    }else if(uv.x<0.5 && uv.y<0.5)
    {
        col = col*vec3(1.0,0.0,0.0);
    }
    
    fragColor = vec4(col,1.0);
}

eg4. 绘制图片,但只绘制中间的一个椭圆区域

效果图如下,新引入函数distance,参数需要两个,这里使用的是vec2,计算两个二维空间点的距离,距离外的用默认颜色【黑色】渲染,距离内的使用纹理颜色渲染。

b2a8679631bdd88b817ff4cda1cec7ad.png

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = vec3(0.0,0.0,0.0);
    
    vec2 tmpUV = uv-vec2(0.5,0.5);  //[]
    float dis = distance(tmpUV,vec2(0.0,0.0));
    if(dis<0.4)
        col = texture(iChannel0,uv).rgb;
    
    
    fragColor = vec4(col,1.0);
}

作者:阿凯  来源:https://zhuanlan.zhihu.com/p/448215305

0d50acf6511d6e7faa79447899eaabb8.png

技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

a264191947f940bfb4396086f551ea8e.png

私信领取相关资料

推荐阅读:

音视频开发工作经验分享 || 视频版

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

Android NDK 免费视频在线学习!!!

你想要的音视频开发资料库来了

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

353792ecdbcfe3a50af8164eb387ae3d.gif

相关文章:

  • 拍乐云首发音视频「分组讨论」开放能力,开启线上群聊互动新玩法
  • 浅入浅出WebGPU
  • Vulkan 在 FFmpeg 中的支持
  • 音视频中的语音信号处理技术
  • 声网3D空间音频技术解析:3D空间音效+空气衰减模拟+人声模糊
  • 音视频春节假期内卷指南(实操)
  • HDR技术趋势浅析
  • 干货收藏 || Vulkan Game Engine 视频教程
  • 详解低延时高音质:丢包、抖动与 last mile 优化那些事儿
  • FFmpeg 音视频倍速控制
  • vertex shader中怎么获取临近顶点的属性值?
  • Seek 策略以及在有 B 帧情况下的处理
  • FFmpeg 中的多线程解码
  • 视频图像色彩增强的主要方法与落地实践
  • 根据采样频率计算音频时长
  • [译]Python中的类属性与实例属性的区别
  • 230. Kth Smallest Element in a BST
  • github从入门到放弃(1)
  • httpie使用详解
  • HTTP中GET与POST的区别 99%的错误认识
  • JavaScript DOM 10 - 滚动
  • JavaScript 一些 DOM 的知识点
  • jdbc就是这么简单
  • ng6--错误信息小结(持续更新)
  • vue的全局变量和全局拦截请求器
  • 分布式熔断降级平台aegis
  • 复习Javascript专题(四):js中的深浅拷贝
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 我建了一个叫Hello World的项目
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 学习JavaScript数据结构与算法 — 树
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • # include “ “ 和 # include < >两者的区别
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (07)Hive——窗口函数详解
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (七)Knockout 创建自定义绑定
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (十三)Maven插件解析运行机制
  • (转)ORM
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .NET 8.0 中有哪些新的变化?
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .Net Core和.Net Standard直观理解
  • .net 发送邮件
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • .NET面试题(二)
  • .NET微信公众号开发-2.0创建自定义菜单