惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
Latest news
Latest news
P
Palo Alto Networks Blog
T
The Exploit Database - CXSecurity.com
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
AI
AI
阮一峰的网络日志
阮一峰的网络日志
量子位
Project Zero
Project Zero
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Schneier on Security
大猫的无限游戏
大猫的无限游戏
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
K
Kaspersky official blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Visual Studio Blog
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
W
WeLiveSecurity
博客园 - Franky
Last Week in AI
Last Week in AI
博客园 - 聂微东
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
IT之家
IT之家
I
Intezer
The Cloudflare Blog
月光博客
月光博客
N
News | PayPal Newsroom
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
博客园_首页
罗磊的独立博客
NISL@THU
NISL@THU
爱范儿
爱范儿
Jina AI
Jina AI
有赞技术团队
有赞技术团队
The Hacker News
The Hacker News
A
Arctic Wolf
Scott Helme
Scott Helme
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed

音视频开发进阶

音视频教程-第三节 音视频教程-第二节 真的,AI 可能就是新时代的信息差 充值 Cursor 之后,工作有了哪些变化?🤔 个人'蒸馏'大模型能做哪些有意思的事情 DeepSeek 大模型在 Mac 上的部署和运行 音视频教程-第一节 【WebRTC 专栏】-- Android 开发集成 WebRTC 库的几种方式 【WebRTC 专栏】-- 在 Mac M1 等系列芯片编译和开发 WebRTC-Android 库 Meta Llama3 大模型在 Mac 上的部署和运行 iOS VideoToolBox 解码 HEVC Open-GOP 视频的问题排查 Flutter 状态管理之 InheritedWidget 使用和分析 用 ChatGPT 回答技术问题怎么样 ? 音视频开发系统入门大致路线 UE 4.27 添加自定义 ShadingModel 用 UE4 虚幻引擎做个捏脸小功能~~ UE4 材质练习 之 凹凸贴图偏移的使用 UE4 材质练习系列基础 OpenGL上下文创建以及共享机制 007 | 播放器系列专栏-解析 MP4 文件读取信息 006 | 播放器系列专栏-在 Mac 上查看 MP4 格式信息 干货 | 快速抽取缩略图是怎么练成的? 关于直播、WebRTC、FFmpeg 的那些事 005 | 播放器系列专栏-在 Windows 上查看 MP4 格式信息 将音视频中的花屏、绿屏、黑屏问题一网打尽 关于音视频里面的解码帧率和渲染帧率 004 | 播放器系列专栏-认识MP4视频(下) 003 | 播放器系列专栏-认识MP4视频(上) 入门或者转行音视频,应该要怎么做? H264视频文件如何缩放分辨率 002 | 播放器系列专栏-FFmpeg依赖库的配置 001 | 播放器系列专栏-关于播放器项目的一个小实践 Seek策略以及在有B帧情况下的处理 目前流媒体开发工程师工作内容主要是什么? 一个音视频领域专业问答的小圈子 干货收藏 || Vulkan Game Engine 视频教程 音视频春节假期内卷指南(实操) Vulkan 在 FFmpeg 中的支持 Windows 下 FFmpeg 和 LibX264 的编译和配置 Metal 开发 | 使用 C++ 进行接口调用 音视频开发工作经验分享 || 视频版 FFmpeg 调用 MediaCodec 硬解码到 Surface 上 代码吸猫 | 用 OpenGL 图像渲染的养猫计划 百倍变速--解码到底能不能丢 非参考帧 ?FFmpeg 有话说!!! 老生常谈-FFmpeg 的编译问题轻松搞定 FFmpeg 调用 Android MediaCodec 进行硬解码(附源码) 【WebRTC 专栏】--创建相机预览 Unity Shader 光照基础之 Half Lambert 光照模型 Unity Shader 光照基础之Lambert光照模型 Unity Shader 光照基础内容 Unity Shader 显示一张图片纹理 UnityShader 的基本概念 Unity 物体的基本操作 C++ 模板系列小结07-尾置返回类型 C++ 模板系列小结06-可变参数模板特性 C++ 中的多线程的使用和线程池建设 C++ 模板系列小结05-模板类型作为模板参数 C++ 模板系列小结04-类模板中的成员模板 C++ 模板系列小结03-在模板中指定变量类型 C++ 模板系列小结02-非类型模板参数 C++ 模板系列小结01-函数模板和类模板 从零打造渲染引擎系列01-什么是渲染引擎 iOS开发 - 在 Swift 中去调用 C/C++ 代码 2021 技术新番 - 从零打造渲染引擎系列 iOS 音视频开发的一些基础准备工作 音视频交流群又来啦~~~ 【WebRTC 专栏】WebRTC & Android 开发学习环境搭建~ 【喜大普奔】域名终于备案通过啦 【音视频连载-011】第二季 FFmpeg 一层一层获取文件信息 KodeLife | Shader 实时编辑预览的强大工具使用实践 推荐几个堪称教科书级别的 Android 音视频入门项目 【音视频连载-010】第二季 FFmpeg 日志打印 【音视频连载-008】基础学习篇-SDL 播放 PCM 音频文件(下) 【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上) 【音视频连载-006】基础学习篇-SDL 播放 YUV 视频文件 【音视频连载-005】基础学习篇-SDL 加载 YUV 文件并显示 【音视频连载-004】基础学习篇-SDL 加载图片并显示 【音视频连载-003】基础学习篇-SDL 消息循环和事件响应 【音视频连载-002】基础学习篇-SDL 创建窗口并显示颜色 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置 LearnOpenGL 源码在 MAC 上的编译与调试 2019 年终总结与回顾 Android NDK 开发的免费技术视频来啦~~ OpenGL 实现视频编辑中的转场效果 OpenGL 实践之贝塞尔曲线绘制 图像库 libjpeg-turbo 编译与实践 图像库 libpng 编译与实践 rust 开发编译 Android 动态库实践 Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理 简单易用的图像解码库介绍 —— stb_image 博客图床迁移记 进击的 Vulkan 移动开发之 SwapChain 进击的 Vulkan 移动开发之 Command Buffer 进击的 Vulkan 移动开发之 Instance & Device & Queue 进击的 Vulkan 移动开发(一)之今生前世 Java 显式锁 Lock 与条件队列 C++ 标准容器库小结 MediaCodec 硬编码之相机内容编码成 H264 文件 一文读懂 YUV 的采样与格式 《OpenGL ES 3.x 游戏开发》碰撞检测之 AABB 包围盒
Shader 优化 | OpenGL 绘制网格效果
音视频开发进阶 · 2020-05-22 · via 音视频开发进阶

一个专注音视频领域的小圈子

避免图片丢失,建议阅读微信原文:

https://mp.weixin.qq.com/s/FmILdXuy3HMpv8H1Kz_fPw 前几天发布了这样一篇文章: KodeLife | Shader 实时编辑预览的强大工具使用实践 除了介绍 KodeLife 的使用之外,还附带了一个 Shader 绘制网格效果的代码。 把这篇文章发到技术群里,随机就有大佬指出不足之处,提示说代码还可以进一步优化,并且提供了源码学习。 可见加入一个高质量的技术群是多么重要,哪怕平时不说话,围观大佬们聊天都能学到很多。 现在加入还来得及,尚有余位,详情点击如下链接: 移动端技术交流喊你入群啦~~~


在我的 Shader 代码中是这样绘制网格的:

    vec2 fragcoord = vec2(gl_FragCoord.xy / u_resolution);
    vec3 bgColor = vec3(1.0,1.0,1.0);
    vec3 pixelColor = bgColor;
    vec3 gridColor = vec3(0.5,0.5,0.5);

    const float width = 0.1;
    const float minWidth = 0.003;
    for(float i = 0.0; i < 1.0; i+=width){
    if (mod(fragcoord.x,width) < minWidth || mod(fragcoord.y,width) < minWidth){
            pixelColor = gridColor;
        }
    }
    gl_FragColor = vec4(pixelColor,1.0);

首先,讲解几个概念:

gl_FragCoord 代表当前像素相对于屏幕的坐标,屏幕左下角为原点。

u_resolution 是当前图像的分辨率。

gl_FragCoord 除以 u_resolution 得到的结果 fragcoord 就是归一化的屏幕坐标。

由于已经归一化了那么 fragcoord 的值就在 [0,1] 的闭区间内。

同时用 gridColor 作为网格的颜色,bgColor 作为背景色,也是默认的颜色,pixelColor 作为最后输出的颜色。

那么,代码的重点就在于 for 循环里面了。

由于 fragcoord 归一化有了确定的值域范围,所以可以在 for 循环中将它十等分。

另外,因为片段着色器每个像素都会执行一遍,每次 fragcoord 值都是变化的,但不管怎么变化,它的范围都会落在 for 循环的十等份里面。

比如其中某一份的范围是 [0.2,0.3) 的左闭右开区间,当前像素就落在这个范围内。

那么 mod 取模函数就会判断当前值距离左区间阈值是否在 minWidth 范围内,其中 minWidth 相当于是指定网格线的宽度。

如果在范围内,那么显示的颜色就是网格色,否则就是默认的背景色。

以上的讲解对于坐标的 xy 值是一样的道理。原理通过判断该像素点的坐标是否位于临界范围内来选择性着色。

显示这种绘制方式是有它的弊端,因为每一个像素执行片段着色器的时候,都要进行一次 for 循环判断它处于哪个区域内。

这样就有了太多不必要的计算流程,尤其是 for 循环的每次遍历。


接下来就是微信群中大佬给出的 Shader 代码:

vec2 st = vec2(gl_FragCoord.xy / u_resolution);
st.x *= u_resolution.x / u_resolution.y;

vec3 color = vec3(.0);

st *= 10.;

vec2 i_st = floor(st);
vec2 f_st = fract(st);

color += step(.98,f_st.x) + step(.98,f_st.y);

gl_FragColor = vec4(color,1.0);

可以一眼看出这里面没有 for 循环的操作了。

还是先讲解几个级别操作:

floor 函数就是向下取整的操作

fract 函数是 x - floor(x) 的操作,也就是取小数部分的意思。

通过对 st 进行 floorfract 操作可以分出它的整数和小数部分。

step 函数类似于 if 判断,当第二个参数大于等于第一个参数,则返回 1 ,否则返回 0 。

整个 Shader 代码第一行还是相同的,都是归一化操作。

然后在第二行

st.x *= u_resolution.x / u_resolution.y

实际上是做了一个比例切换的操作。将 stxy 值按照图像分辨率的宽高比做了调整,其中以 y 为基准 1 。

这样一来,sty 值还是在 [0,1] 范围内,而 x 值可能大于也可能小于这个范围了,这都取决于图像分辨率了。

接下来将 st 乘了 10 ,这下 st 的值域范围就在 [0,10] 了 ,这样的操作是为了接下来的 floor 函数,因为它是取整,如果都在 [0,1] 范围内,取的整数永远都是 0 了。

前面转换操作是为了接下来的重点函数 step

color += step(.98,f_st.x) + step(.98,f_st.y);

前面的 floor 其实也是将 xy 轴做了等分,比如 y 的值域是 [0,1] ,乘以 10 之后,就是十等分,x 的值域如果是 [0,1.7] ,乘以 10 之后,就是十七等分。

fract 操作的结果范围必然是 [0,1) 的左闭右开区间。

step 函数的意图就是如果该像素点的坐标接近于等分线,那么 color 的颜色值返回的就是 1 ,显示白色,否则返回 0 ,显示黑色。

比如,st 的 x 值是 7.99 了,接近于 8 ,那么就要显示白色网格线了,对于 y 值同理。

这样一来就可以对每个像素点进行判断,根据它的坐标决定要显示什么颜色。

总结对比

在第二种绘制中,由于做了比例转换操作,所以绘制出来的网格大小都是一致的,且都是正方形。

而第一种没有比例切换操作,当宽高不同的情况下,同样进行十等分的话,画出来的网格是个长方形了。

但是,两种绘制的思路都是相同的,姑且称它为 接近法 吧,当绘制的像素接近等分线时,就显示不一样的颜色。

于是,等分线的操作思路就各有不同了。前者是利用 for 循环来制造划分,后者则是利用当前像素的 xy 值的特点来绘制的。

当然更推崇后者的绘制方式了,也是学到了新技巧~~~

能力不足,经验有限,文中有何不对的地方欢迎批评指正,也可以加我微信 ezglumes 交流~~

原创文章,转载请注明来源:    Shader 优化 | OpenGL 绘制网格效果