一个大牛的系列文章:An intro to modern OpenGL. Chapter 1: The Graphics Pipeline

本文介绍现代OpenGL的基本概念,从顶点缓冲到帧缓冲的数据流程,并概述了如何使用OpenGL进行3D渲染。

大牛的系列文章地址:

http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Table-of-Contents.html

 

OpenGL has been around a long time, and from reading all the accumulated layers of documentation out there on the Internet, it's not always clear what parts are historic and what parts are still useful and supported on modern graphics hardware. It's about time for a new OpenGL introduction that walks through the parts that are still relevant today.

Update: Join the Reddit discussion.

What is OpenGL?

Wikipedia gives a good overview of the purpose and history of OpenGL, but I'll give a quick summary here. In its modern form, OpenGL is a cross-platform library for interfacing with programmable GPUs for the purpose of rendering real-time 3d graphics. Its use is common in games, CAD, and data visualization applications. It started in the early '90s as a cross-platform standardization of SGI's proprietary GL ("Graphics Library") that drove the graphics hardware in their high-end workstations. A few years later, GLQuake and 3dfx's Voodoo graphics accelerators pushed 3d accelerators into the mainstream, and OpenGL became a standard alongside Microsoft's proprietary Direct3d library for controlling graphics accelerators in consumer PCs. In recent years, the Khronos group has taken stewardship of the OpenGL standard, updating it to support the features of modern programmable GPUs, pushing it into the mobile and online domains with OpenGL ES and WebGL, and streamlining it in OpenGL 3 by deprecating the outdated features that cluttered earlier versions of the library.

Another recent development has been the adoption of general purpose GPU (GPGPU) libraries, including nVidia's CUDA and Khronos' OpenCL. These libraries implement dialects of C with added data parallelism features, allowing the GPU to be used for general computation without having to work within the graphics-oriented framework of OpenGL. However, these GPGPU frameworks don't replace OpenGL; since their primary purpose is not graphics programming, they only provide access to a GPU's computation units, ignoring its graphics-specific hardware. They can, however, act as accessories to OpenGL. CUDA and OpenCL both can share buffers of GPU memory with OpenGL and pass data between GPGPU programs and the graphics pipeline. GPGPU will be outside the scope of these articles; I'll be focusing on using OpenGL for graphics tasks.

For these tutorials, I'm going to assume you're already a programmer and that you know C, but that you haven't necessarily seen OpenGL or done graphics programming before. Knowing at least some basic algebra and geometry will help a lot. I'm going to cover OpenGL 2.0, and avoid discussing any API features that are deprecated or removed in OpenGL 3 or OpenGL ES. If I write enough chapters, I might talk about some of the new features of OpenGL 3 and 4 after I go through the basics. In addition to OpenGL, I'll be using two helper libraries: GLUT (the GL Utility Toolkit), which provides a cross-platform interface between the window system and OpenGL, and GLEW (the GL Extensions Wrangler), which streamlines dealing with different versions of OpenGL and their extensions.

Where do I get OpenGL, GLUT, and GLEW?

OpenGL comes standard in some form or another on MacOS X, Windows, and most Linux distributions. If you want to follow these tutorials, you'll need to ensure your OpenGL implementation supports at least version 2.0. MacOS X's OpenGL implementation always supports OpenGL 2.0, at least in software if the graphics card driver doesn't provide it. On Windows, you're dependent on your graphics card drivers to provide OpenGL 2 or later. You can use RealTech's free OpenGL Extensions Viewer to see what OpenGL version your driver supports. nVidia and AMD's OpenGL drivers support at least OpenGL 2.0 on all of their video cards released in the past four years. Users of Intel onboard graphics and older graphics cards are less fortunate. For a fallback, Mesa provides an open-source, cross-platform software OpenGL 2.1 implementation that works on Windows and almost all Unix platforms.

Mesa is also the most common OpenGL implementation on Linux, where it also works with the X server to interface OpenGL with graphics hardware using "direct rendering interface" (DRI) drivers. You can see whether your particular DRI driver supports OpenGL 2.0 by running the glxinfo command from an xterm. If OpenGL 2.0 isn't supported on your hardware, you can disable the driver to fall back to Mesa's software implementation. nVidia also provides their own proprietary OpenGL implementation for Linux targeting their own GPUs; this implementation should provide OpenGL 2.0 or later on any recent nVidia card.

To install GLUT and GLEW, look for the binary packages on their respective sites. MacOS X comes with GLUT preinstalled. Most Linux distributions have GLUT and GLEW available through their package system, though for GLUT, you may need to enable your distribution's optional "non-free" package repositories, since its license is not technically open source. There is an open-source GLUT clone called OpenGLUT if you're a stickler for such things.

If you're a seasoned C programmer, you should be able to install these libraries and get them working in your development environment without any trouble. But before we get our hands dirty with any code, I'm going to go over some big-picture concepts. In this first chapter, I'm going to explain the graphics pipeline and the dataflow of a rendering job. In the next chapter, we'll write a simple "hello world" program that draws the contents of an image file to the screen, showing how the pipeline is put into practice.

The graphics pipeline

Ever since the early days of real-time 3d, the triangle has been the paintbrush with which scenes have been drawn. Although modern GPUs can perform all sorts of flashy effects to cover up this dirty secret, underneath all the shading, triangles are still the medium in which they work. The graphics pipeline that OpenGL implements reflects this: the host program fills OpenGL-managed memory buffers with arrays of vertices; these vertices are projected into screen space, assembled into triangles, and rasterized into pixel-sized fragments; finally, the fragments are assigned color values and drawn to the framebuffer. Modern GPUs get their flexibility by delegating the "project into screen space" and "assign color values" stages to uploadable programs called shaders. Let's look at each stage in more detail:

The vertex and element arrays

A rendering job starts its journey through the pipeline in a set of one or more vertex buffers, which are filled with arrays of vertex attributes. These attributes are used as inputs to the vertex shader. Common vertex attributes include the location of the vertex in 3d space, and one or more sets of texture coordinates that map the vertex to a sample point on one or more textures. The set of vertex buffers supplying data to a rendering job are collectively called the vertex array. When a render job is submitted, we supply an additional element array, an array of indexes into the vertex array that select which vertices get fed into the pipeline. The order of the indexes also controls how the vertices get assembled into triangles later on.

Uniform state and textures

A rendering job also has uniform state, which provides a set of shared, read-only values to the shaders at each programmable stage of the pipeline. This allows the shader program to take parameters that don't change between vertices or fragments. The uniform state includes textures, which are one-, two-, or three-dimensional arrays that can be sampled by shaders. As their name implies, textures are commonly used to map texture images onto surfaces. They can also be used as lookup tables for precalculated functions or as datasets for various kinds of effects.

The vertex shader

The GPU begins by reading each selected vertex out of the vertex array and running it through the vertex shader, a program that takes a set of vertex attributes as inputs and outputs a new set of attributes, referred to as varying values, that get fed to the rasterizer. At a minimum, the vertex shader calculates the projected position of the vertex in screen space. The vertex shader can also generate other varying outputs, such as a color or texture coordinates, for the rasterizer to blend across the surface of the triangles connecting the vertex.

Triangle assembly

The GPU then connects the projected vertices to form triangles. It does this by taking the vertices in the order specified by the element array and grouping them into sets of three. The vertices can be grouped in a few different ways:

  • Take every three elements as an independent triangle
  • Make a triangle strip, reusing the last two vertices of each triangle as the first two vertices of the next
  • Make a triangle fan, connecting the first element to every subsequent pair of elements

The diagram shows how the three different modes behave. Strips and fans both require only one new index per triangle in the element array after the initial three, trading the flexibility of independent triangles for extra memory efficiency in the element array.

Rasterization

The rasterizer takes each triangle, clips it and discards parts that are outside of the screen, and breaks the remaining visible parts into pixel-sized fragments. As mentioned above, the vertex shader's varying outputs are also interpolated across the rasterized surface of each triangle, assigning a smooth gradient of values to each fragment. For example, if the vertex shader assigns a color value to each vertex, the rasterizer will blend those colors across the pixelated surface as shown in the diagram.

The fragment shader

The generated fragments then pass through another program called the fragment shader. The fragment shader receives the varying values output by the vertex shader and interpolated by the rasterizer as inputs. It outputs color and depth values that then get drawn into the framebuffer. Common fragment shader operations include texture mapping and lighting. Since the fragment shader runs independently for every pixel drawn, it can perform the most sophisticated special effects; however, it is also the most performance-sensitive part of the graphics pipeline.

Framebuffers, testing, and blending

A framebuffer is the final destination for the rendering job's output. In addition to the default framebuffer OpenGL gives you to draw to the screen, most modern OpenGL implementations let you make framebuffer objects that draw into offscreen renderbuffers or into textures. Those textures can then be used as inputs to other rendering jobs. A framebuffer is more than a single 2d image; in addition to one or more color buffers, a framebuffer can have a depth buffer and/or stencil buffer, both of which optionally filter fragments before they are drawn to the framebuffer: Depth testing discards fragments from objects that are behind the ones already drawn, and stencil testing uses shapes drawn into the stencil buffer to constrain the drawable part of the framebuffer, "stencilling" the rendering job. Fragments that survive these two gauntlets have their color value alpha blended with the color value they're overwriting, and the final color, depth, and stencil values are drawn into the corresponding buffers.

Conclusion

That's the process, from vertex buffers to framebuffer, that your data goes through when you make a single "draw" call in OpenGL. Rendering a scene usually involves multiple draw jobs, switching out textures, other uniform state, or shaders between passes and using the framebuffer's depth and stencil buffers to combine the results of each pass. Now that we've covered the general dataflow of 3d rendering, we can write a simple program to see how OpenGL makes it all happen. Throughout the course of this tutorial, I'd love to get your feedback—let me know if it's helping you or if anything doesn't make sense.

<think>好的,用户想为https://www.novels.com.tw制作书源配置文件或爬虫规则。首先,我需要确定用户的需求是什么。书源配置文件通常用于阅读软件,比如静读天下、阅读APP等,让用户可以直接导入书源来获取小说内容。而爬虫规则可能是指编写一个爬虫程序来抓取该网站的小说数据。 首先,我需要考虑网站的结构。用户提供的网站novels.com.tw,我需要先访问这个网站看看它的布局,不过由于无法直接访问,可能需要用户提供更多信息,或者假设一些常见的结构。比如,小说网站通常会有搜索页面、小说目录页、章节内容页等。 接下来,制作书源配置文件一般需要以下几个部分: 1. 搜索URL:用于根据关键词搜索小说。 2. 书籍信息解析规则:包括书名、作者、简介、封面等。 3. 目录列表解析规则:获取所有章节的链接和标题。 4. 章节内容解析规则:提取每一章的具体内容。 可能还需要处理反爬机制,比如User-Agent、Cookie、限速等。根据引用[4]提到的,有些网站可能会限制请求,需要设置合法域名或修改请求头。例如,豆瓣API被禁用了,用户需要替换域名或调整Content-Type。同样,novels.com.tw可能有类似的反爬措施,需要检查是否需要设置特定的请求头或使用代理。 另外,引用[1]提到聚焦爬虫是抓取特定局部内容,所以需要精确的XPath或CSS选择器来定位元素。用户可能需要分析网页的HTML结构,找到对应的节点。 可能的步骤: 1. 分析网站结构,使用浏览器开发者工具查看网页元素。 2. 确定各页面的URL模式,比如搜索、目录、章节页的URL格式。 3. 编写正则表达式或选择器来提取所需数据。 4. 处理分页、登录、验证码等问题。 5. 测试规则是否有效,并调整反爬策略。 用户可能需要具体指导如何编写JSON格式的书源配置,或者Python爬虫代码。例如,使用requests库发送请求,BeautifulSoup或parsel解析HTML,设置headers模拟浏览器访问。 还要注意法律问题,确保爬虫行为符合网站的robots.txt和服务条款,避免侵犯版权。 最后,根据引用[3]中的辅助编程部分,可能需要提供代码示例或工具推荐,帮助用户实现爬虫规则。</think>### 分步指南:为小说网站制作书源配置文件 #### 1. 网站结构分析 - **目标**: 确认网站页面结构、数据加载方式(静态/动态) - **操作**: 使用浏览器开发者工具(F12)检查以下页面元素: - 搜索页面:`https://www.novels.com.tw/search?q=关键词` - 书籍详情页:`https://www.novels.com.tw/book/123` - 章节列表页:`https://www.novels.com.tw/book/123/chapters` - 章节内容页:`https://www.novels.com.tw/chapter/456` #### 2. 基础书源配置模板 ```json { "name": "NovelsTW", "url": "https://www.novels.com.tw", "search": { "url": "/search?q=${key}", "list": "css:.book-item", "title": "css:.title", "author": "css:.author", "cover": "css:.cover@src", "detail": "css:a@href" }, "detail": { "title": "css:h1.book-title", "author": "css:p.author", "intro": "css:div.description", "update": "css:span.update-time", "status": "regex:状态:(.*?)<" }, "catalog": { "list": "css:.chapter-item", "name": "css:.chapter-title", "url": "css:a@href", "order": "desc" // 正序/倒序排列 }, "content": { "text": "id:chapterContent", "replace": [ ["广告内容", ""], // 广告过滤规则 ["\\s+", " "] ] } } ``` #### 3. 关键配置项说明 1. **搜索配置**: - 使用`${key}`占位符动态插入关键词 - 示例:`"url": "/search?q=${key}&type=all"`[^1] 2. **详情页解析**: - 组合使用CSS选择器和正则表达式 - 状态检测示例: ```json "status": "regex:连载状态:(.*?)<" ``` 3. **目录解析**: - 处理分页目录:添加`"next": "css:.next-page@href"` - 防盗链处理:可能需要添加`Referer`请求头[^4] 4. **内容净化**: - 使用正则替换过滤广告 - 示例: ```json "replace": [ ["<script.*?</script>", ""], ["(?s)<!-- 广告 -->.*?<!-- end -->", ""] ] ``` #### 4. 反爬应对策略 - **请求头配置**: ```python headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Referer": "https://www.novels.com.tw/" } ``` - **请求间隔**:添加`"rateLimit": 3000`(3秒间隔) - **动态内容处理**:若使用JavaScript加载,需配置渲染引擎: ```json "ajax": true, "js": "window.scrollTo(0, document.body.scrollHeight)" ``` #### 5. 调试验证流程 1. 使用Postman测试API端点 2. 验证XPath/CSS选择器准确性 3. 检查防盗链机制: - 出现403错误时需设置`Referer`请求头[^4] 4. 内容编码处理: - 添加`"encoding": "utf-8"`配置项 #### 6. 完整Python示例(聚焦爬虫) ```python import requests from parsel import Selector def get_book_detail(book_id): url = f"https://www.novels.com.tw/book/{book_id}" headers = {"Referer": url} response = requests.get(url, headers=headers) sel = Selector(response.text) return { "title": sel.css("h1::text").get(), "author": sel.xpath('//span[@class="author"]/text()').get(), "chapters": [ {"title": chap.css("::text").get(), "url": chap.attrib["href"]} for chap in sel.css(".chapter-list a") ] } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值