音频队列服务编程指南(Audio Queue Services Programming Guide)(二)

关于音频队列(Audio Queues)

本章你将学习到音频队列的功能、架构和内部工作原理。我们将向你介绍音频队列用来播放或录制所用的音频队列(audio queues)、音频队列缓冲区(audio queue buffers)和回调函数(callback functions),你还可以找到关于音频队列状态和参数的信息,截至到本章的结尾,你将会获得有效使用该技术的概念性理解。

什么是音频队列?

    在iOS和Mac OS X中,音频队列是一个用来录制和播放音频的软件对象,他用AudioQueueRef这个不透明数据类型来表示,该类型在AudioQueue.h头文件中声明。

    音频队列完成以下工作:

  • 连接音频硬件

  • 内存管理

  • 根据需要为已压缩的音频格式引入编码器

  • 媒体的录制或播放

    你可以将音频队列配合其他Core Audio的接口使用,再加上相对少量的自定义代码就可以在你的应用程序中创建一套完整的数字音频录制或播放解决方案。

音频队列架构

    所有的音频队列都含有相同的基础结构,包含以下几部分:

  • 一组音频队列缓冲区(audio queue buffers),每个音频队列缓冲区都是一个存储音频数据的临时仓库

  • 一个缓冲区队列(buffer queue),一个包含音频队列缓冲区的有序列表

  • 一个你自己编写的音频队列回调函数(audio queue callback)

    它的架构很大程度上依赖于这个音频队列是用来录制还是用来播放的。不同之处在于音频队列如何连接到它的输入和输入,还有它的回调函数所扮演的角色。
用来录制的音频队列

    录制用音频队列,用AudioQueueNewInput函数创建,拥有如图1-1的结构


图示 1-1   录制用音频队列 A recording audio queue

    录制用音频队列的输入端一般连接到外部的音频硬件上,比如说麦克风。在iOS中,音频来自于由用户连接的设备--内置的麦克风或者耳机麦克风,比如说,在Mac OS X默认情况下,音频来自于由用户在系统首选项中设置的系统默认音频输入设备。

    录制用音频队列的输入端利用了你自己写的回调函数,当录制音频到磁盘上的时候,回调函数将存有从音频队列中接收到的新的音频数据的缓冲区写入到音频文件中。然而,录制用音频队列也可以用其他方法来使用。你也可以使用其中一种,比如说,在一个实时的分析仪中,在这种情况下,你的回调函数会直接向你的应用程序提供音频数据,而不是将它写入磁盘。

    你将在“记录用音频队列回调函数”中学到更多关于这个回调的知识。

    每一个音频队列——无论是录制用还是播放用——都有一个或多个音频队列缓冲区。这些缓冲区排列在一个特殊的被称为缓冲区队列(buffer queue)的序列中。如图所示,音频队列缓冲区是按照他们被填充的顺序编号的——这也是和把他们交付给回调函数的顺序是相同的。你将在“缓冲区队列和入队”中学到如何音频队列是如何使用它的缓冲区的。

用来播放的音频队列

    播放用音频队列(用AudioQueueNewOutput函数创建)拥有如图1-2所示的结构


图示 1-2   播放用音频队列 A playback audio queue

    在播放用音频队列中,回调函数是在输入端的,这个回调函数的职责就是从磁盘(或其他来源)中获取音频数据,然后将它交付给音频队列。当没有更多音频数据需要播放的时候告诉音频队列停止,你将在“播放用音频队列回调函数”中学到更多关于这个回调函数的知识。

    播放用音频队列的输出端一般都是连接到外部的音频设备的,比如说扬声器。在iOS中,音频通过用户选择的设备播放——比如说,接受者是耳机。在Mac OS X中,默认情况下,音频会通过用户在系统首选项中设置的默认音频输出设备中输出。

音频队列缓冲区

    音频队列缓冲区(audio queue buffer)是一个AudioQueueBuffer类型的数据结构,声明于AudioQueue.h头文件。

[cpp]  view plain copy
  1. <span style="font-size:12px;">typedef struct AudioQueueBuffer {  
  2.     const UInt32   mAudioDataBytesCapacity;  
  3.     <strong>void *const    mAudioData;</strong>  
  4.     UInt32         mAudioDataByteSize;  
  5.     void           *mUserData;  
  6. } AudioQueueBuffer;  
  7. typedef AudioQueueBuffer *AudioQueueBufferRef;</span>  
    上述代码中高亮的 mAudioData 域,指向了缓冲区本身:一个用来当作暂时存放录制或播放音频数据的容器的内存,其他域中的数据用来辅助音频队列管理这个缓冲区。

    音频队列可以使用任意数量的缓冲区。你的应用程序制定它的数量。一般情况下这个数字是3。这样就可以让给一个忙于将数据写入磁盘,同时另一个在填充新的音频数据,第三个缓冲区在需要做磁盘I/O延迟补偿的时候可用。图示1-3演示了这个过程。

    音频队列负责对它的缓冲区进行内存管理

  • 当你调用AudioQueueAllocateBuffer函数的时候音频队列创建了一个缓冲区

  • 当你通过调用AudioQueueDispose函数释放一个音频队列的时候,这个音频队列释放掉它拥有的缓冲区

    这提高了你添加到应用程序中的录制和播放功能的鲁棒性。同时它也帮助你优化了资源的使用。

    关于AudioQueueBuffer数据结构的完整描述,请参照音频队列服务参考(Audio Queue Services Reference)

缓冲区队列和入队

    传递给音频队列的缓冲区队列,就如它的名字一样,就是事实上的音频队列服务(Audio Queue Services),你见过缓冲区队列——一个缓冲区的有序列表——在“音频队列架构”中你学到了音频队列对象如何配合回调函数在录制或播放的过程中管理缓冲区队列。特别的,你将学习到入队(enqueuing),缓冲区队列对音频队列缓冲区的附加操作。无论你正在实现录制或者播放,入队都是你在回调函数中需要执行的任务。

录制过程

    当进行录制的时候,一个音频队列缓冲区被填充了从列入例如麦克风的输入设备种获取的音频数据。缓冲区队列中的其他缓冲区将在当前缓冲区的后面依次排队等待被填充音频数据。

    音频队列将按照缓冲区填充的顺序把已填充过音频数据的缓冲区交付给你的回调函数。图示1-3演示了当使用音频队列的时候这个录制过程是如何工作的。

图示 1-3  录制过程The recording process

    在图示1-3中的第一步,录制开始,音频队列用获取到的数据填充缓冲区。

    第二步,第一个缓冲区填充完毕,音频队列调用回调函数来处理这个被填充满的缓冲区(缓冲区一)。回调函数(第三步)将缓冲区的内容写到音频文件中。同时,音频队列将另一个缓冲区(缓冲区二)填充新获取到的数据。

    在第四步,回调函数将刚刚写入磁盘的缓冲区(缓冲区一)入队,使它重新重新回到被填充的队列。音频队列再一次调用回调函数(第五步),处理下一个填充完毕的缓冲区(缓冲区二)。回调函数(第六步)将这个缓冲区的内容写入到音频文件。这种稳定状态会一直持续到用户停止录制。

播放过程

    当进行播放的时候,音频队列缓冲区将被传送到像扬声器这样的输出设备。缓冲区队列中其他的缓冲区讲按顺序排在当前缓冲区后面等待播放。

    音频队列将已经播放过的音频数据按照他们播放的顺序交付给你的回调函数,回调函数将新的音频数据读取到一个缓冲区中,然后将它入队。图示1-4演示了当使用音频队列时播放是如何工作的

图示 1-4  播放过程Illustration of the playback process when using an audio queue

    图示1-4中的第一步,应用程序启动播放用音频队列,应用程序对每一个音频队列缓冲区调用回调函数,填充这些缓冲区并且将它们加入缓冲区队列。启动操作会确保播放可以立即执行当你的应用程序调用AudioQueueStart函数之后。

    在第三步,音频队列将第一个缓冲区(缓冲区一)交付给输出。

    当第一个缓冲区被播放完毕之后,播放用音频队列就进入了一个稳定的循环状态。音频队列开始播放下一个缓冲区(第四步,缓冲区二)然后调用回调函数(第五步),处理刚刚播放完的那个缓冲区(缓冲区一)。这个回调函数(第六步)从音频文件中读取数据填充缓冲区然后将他们入队用于播放。

控制播放过程

    音频队列缓冲区总是按照他们入队的顺序进行播放,然而,在播放过程中,音频队列服务为你提供了AudioQueueEnqueueBufferWithParameters函数来进行一些控制,这个函数有以下功能:

  • 设置缓冲区的精确播放时间,这可以让你支持同步

  • 截断音频队列缓冲区开头或结尾的帧(frame),这可以让你移除开头或结尾的静音

  • 在缓冲区的粒度上设置播放增益

    关于更多播放增益的知识,请看"音频队列参数"( Audio Queue Parameters. ),如果要对 Audio Queue Parameters. 函数的完整描述,请参照“音频队列服务参考”( Audio Queue Parameters. )。

音频队列回调函数

    一般来说,使用音频队列服务的大部分编程任务都在编程音频队列回调函数上。

    在录制或播放过程中,音频队列将反复的调用它所拥有的音频队列回调函数。调用的时间间隔取决于音频队列缓冲区的容量,并且一般来一说这个时间在半秒或者几秒。

    无论对于录制或者播放,音频队列回调的一个职责就是返回一个缓冲区队列的音频队列缓冲区。回调函数使用AudioQueueEnqueueBuffer函数将一个缓冲区加入到缓冲区队列的末尾。对于播放来说,你也可以使用AudioQueueEnqueueBufferWithParameters函数来获得更多的控制,就像“控制播放过程”中描述的一样。

录制用音频队列回调函数

    本节介绍了一般情况下——将音频录制到磁盘上,这种情况的回调函数。这里是这个录制用回调函数的原型,就和AudioQueue.h头文件中声明的一样:

[cpp]  view plain copy
  1. AudioQueueInputCallback (  
  2.     void                               *inUserData,  
  3.     AudioQueueRef                      inAQ,  
  4.     AudioQueueBufferRef                inBuffer,  
  5.     const AudioTimeStamp               *inStartTime,  
  6.     UInt32                             inNumberPacketDescriptions,  
  7.     const AudioStreamPacketDescription *inPacketDescs  
  8. );  
    录制用音频队列,在调用回调函数的时候,提供了回调函数将下一组音频数据写入到文件的一切信息。
  • inUserData,通常是一个你创建用来保存音频队列和它的缓冲区状态信息的自定义结构,一个音频文件对象 (AudioFileID类型)代表你正在写入的文件,还有这个文件的音频格式信息。

  • inAQ 是调用回调函数的音频队列

  • inBuffer 是一个被音频队列填充新的音频数据的音频队列缓冲区,它包含了回调函数写入文件所需要的新数据。. 数据已经根据你在自己指定的自定义结构(由inUserData参数传入)中指定的格式格式化。关于此点的更多信息,请参照“使用编码器和音频数据格式”

  • inStartTime 是缓冲区中的一采样的参考时间,对于基本的录制,你的毁掉函数不会使用这个参数

  • inNumberPacketDescriptions 是inPacketDescs参数中包描述符(packet descriptions)的数量,如果你正在录制一个VBR(可变比特率(variable bitrate))格式, 音频队列将会提供这个参数给你的回调函数,这个参数可以让你传递给AudioFileWritePackets函数. CBR (常量比特率(constant bitrate)) 格式不使用包描述符。对于CBR录制,音频队列会设置这个参数并且将inPacketDescs这个参数设置为NULL

  • inPacketDescs 是一组对应于缓冲区中采样的包描述符,音频队列提供了这个参数的值,如果音频文件是VBR格式的,你的回调函数可以将这个值传递给AudioFileWritePackets函数(声明于AudioFile.h头文件中)

    如果要了解更多关于录制用回调函数的信息,请参照本文档的 “录制音频” ,并且参照音频队列服务参考(Audio Queue Services Reference.)

播放用音频队列回调函数

    本节介绍了一般情况下——从磁盘文件播放音频,这种情况的回调函数。 这里是这个播放用回调函数的原型,就和AudioQueue.h头文件中声明的一样:

[cpp]  view plain copy
  1. AudioQueueOutputCallback (  
  2.     void                  *inUserData,  
  3.     AudioQueueRef         inAQ,  
  4.     AudioQueueBufferRef   inBuffer  
  5. );  

    播放用音频队列,在调用回调函数的时候,提供了回调函数将下一组音频数据进行读取进行播放的信息。

  • inUserData域,一般来说是一个你创建的包含音频队列和它的缓冲区的的状态信息的自定义结构,一个音频文件对象 (AudioFileID类型) 代表了你要写入的文件和文件的音频数据格式信息。

    在播放音频队列的情况下,回调函数会在这个结构中用一个域保持对当前包的索引

  • inAQ域是调用这个回调函数的音频队列

  • inBuffer域是一个音频队列缓冲区,是一个有音频队列变成可用状态的音频队列缓冲区,你的回调函数将把它填充上下一组要进行播放的音频数据。

    如果你的应用程序在播放VBR数据,回调函数需要得到正在播放的音频数据的包数据,它通过调用 AudioFileReadPackets 函数来完成这个任务,这个函数声明于 AudioFile.h 头文件,回调函数随后将包信息放到自定义的数据结构中以使得它对播放用音频队列可用。

    关于播放回调的更多信息,请看本文档的“播放音频”(Playing Audio),并且参照音频队列服务参考(Audio Queue Services Reference.)

使用编码和音频数据格式

    音频队列服务根据在不同的音频格式之间转换的时候会根据需要使用编码器(音频数据编码/解码组件)。你的录制或播放程序可以使用任意已经安装过相应编码器的格式,不需要写自定义的代码来处理各种各样的音频格式。特别的,你的回调函数不需要知道数据格式。

    现在来讲解一下这是如何工作的,每一个音频队列在AudioStreamBasicDescription结构中都有一个域代表了音频数据格式。当你在mFormatID域中指定了它的格式的时候——音频队列会使用相应的解码器。然后你指定采样率和声道数,这些就是所有你需要做的。你将会在“录制音频”和“播放音频”中看到如何设置音频数据格式的示例。

    录制用音频队列按照图示1-5中的流程使用已安装的编码器。

图示 1-5   在录制音频的时候进行音频格式转换 Using a code when recording with an audio queue




    在图示1-5中的第一步,你的应用程序告诉音频队列开始录制,同时也告诉它所要使用的音频格式。在第二部,音频队列获取新的音频数据,并且根据你指定的格式使用相应的编码器转换音频数据。然后音频队列调用回调函数,将适当的格式化过的音频数据放进缓冲区中。第三步,回调函数将格式化后的音频数据写入磁盘。再次,你的回调函数不需要了解数据格式。

    播放用音频队列按照图示1-6的流程使用已安装的编码器。

图示 1-6  在播放过程中进行音频格式转换Using a codec when playing a file with an audio queue

    在图示1-6的第一步中,你的应用程序告诉音频队列开始播放,同时也告诉了它将要播放放的音频文件的数据格式。在第二步,音频队列调用回调函数来从音频文件中读取音频数据。回调函数按照它的原始格式将音频数据交付给音频队列。在第三步,音频队列使用对应的解码器将音频交付给目标输出。

          音频队列可以使用任意已安装的编码器,无论是Mac OS X原生的还是第三方的。你可以通过指定音频队列的AudioStreamBasicDescription结构中四字节的编码ID来指定将要使用的编码器。你将会在“录制音频”中看到这个字段的使用示例。

    Mac OS X包含大量的编码器,他们都在CoreAudioTypes.h头文件中的format IDs枚举值中列出了,并且记录在核心音频数据类型参考(Core Audio Data Types Reference)中。你可以通过调用AudioFormat.h头文件中的接口来查询当前系统可用的编码器。在Audio Toolbox框架中。你可以使用Fiendishthngs应用程序来显示系统的编码器,相应的示例代码在网址http://developer.apple.com/samplecode/Fiendishthngs/.

音频队列的控制和状态

    音频队列的生命周期在创建和处理之间。你的应用程序管理它的生命周期——并且控制音频队列的状态——通过使用AudioQueue.h头文件中的六个函数:

  • Start(AudioQueueStart). 调用它来初始化录制或者播放

  • Prime (AudioQueuePrime). 对于播放, 在调用AudioQueueStart之前调用这个函数,用来确定音频队列中立刻就有可用的数据来播放。这个函数不在录制中使用

  • Stop(AudioQueueStop).调用这个函数来重置音频队列 (参考下面对AudioQueueReset的描述),然后停止录制或播放。一个播放用音频队列回调函数当它没有更多的数据播放的时候会调用这个函数

  • Pause(AudioQueuePause).调用这个函数可以在不影响缓冲区和不重置音频队列的情况下停止录制或播放。如果需要恢复,调用AudioQueueStart 函数

  • Flush (AudioQueueFlush). 在将最后一个音频队列缓冲区入队之后调用,来确保所有缓存过的数据,也包括处理的中间数据,得到录制或播放

  • Reset (AudioQueueReset). 调用这个函数可以立即让音频队列静音。移除之前调度过的缓冲区,并且重置所有解码器和DSP状态

    你可以在同步或异步模式下使用 AudioQueueStop 函数:
  • 同步立刻停止,不考虑之前缓冲的音频数据

  • 异步在所有已入队的缓冲区播放或录制完毕之后再停止

    参照 Audio Queue Services Reference 来获取所有这些函数的完整描述,也包含了更多关于同步和异步停止音频队列的信息

音频队列参数(Audio Queue Parameters)

    音频队列有一个称作参数(parameters)的可调整设置。每个参数都有一个枚举值作为它的键,一个浮点数作为它的值。参数一般于播放,不用于录制。

    在Mac OS X10.5中,只有一个音频队列参数就是播放增益。可以通过使用kAudioQueueParam_Volume常量来获取或设置它的值,它的有效范围在0.0(静音)到1.0(单位增益)

    你的应用程序可以通过两种方法来设置音频队列参数:

  • 对于每一个音频队列,使用AudioQueueSetParameter函数,这可以让你直接改变音频队列的设置,这个改变是立刻生效的

  • 对于每一个音频队列缓冲区,调用AudioQueueEnqueueBufferWithParameters函数。这可以让你在将音频队列缓冲区入队的时候设置音频队列设置。这种改变只会在播放这个音频队列缓冲区的时候生效。

    这两种情况下,音频队列的参数设置会一直保留到你改变它们为止。

      你可以通过调用AudioQueueGetParameter函数来获取音频队列当前的参数。参考Audio Queue Services Reference 来获得这个函数的完整描述和获取和设置参数值的方法


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
文件太大,分割成两个文件了。 原书名:WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 原出版社: Addison-Wesley Professional 作者: (美)Kouichi Matsuda Rodger Lea(松田浩一,罗杰.李) 译者: 谢光磊 出版社:电子工业出版社 ISBN:9787121229428 上架时间:2014-6-11 出版日期:2014 年6月 开本:16开 页码:470 版次:1-1 ---------------------------------------- 目录 《WebGL编程指南》 第1 章 WebGL 概述 1 WebGL 的优势 3 使用文本编辑器开发三维应用 3 轻松发布三维图形程序 4 充分利用浏览器的功能 5 学习和使用WebGL 很简单 5 WebGL 的起源 5 WebGL 程序的结构 6 总结 7 第2 章 WebGL 入门 9 Canvas 是什么? 10 使用[canvas] 标签 11 DrawRectangle.js 13 最短的WebGL 程序:清空绘图区 16 HTML 文件(HelloCanvas.html) 16 JavaScript 程序(HelloCanvas.js) 17 用示例程序做实验 22 绘制一个点(版本1) 22 HelloPoint1.html 24 HelloPoint1.js 24 着色器是什么? 25 使用着色器的WebGL 程序的结构 27 初始化着色器 29 顶点着色器 31 片元着色器 33 绘制操作 34 WebGL 坐标系统 35 用示例程序做实验 37 绘制一个点(版本2) 38 使用attribute 变量 38 示例程序(HelloPoint2.js) 39 获取attribute 变量的存储位置 41 向attribute 变量赋值 42 gl.vertexAttrib3f() 的同族函数 44 用示例程序做实验 45 通过鼠标点击绘点 46 示例程序(ClickedPoints.js) 47 注册事件响应函数 48 响应鼠标点击事件 50 用示例程序做实验 53 改变点的颜色 55 示例程序(ColoredPoints.js) 56 uniform 变量 58 获取uniform 变量的存储地址 59 向uniform 变量赋值 60 gl.uniform4f() 的同族函数 61 总结 62 第3 章 绘制和变换三角形 63 绘制多个点 64 示例程序(MultiPoint.js) 66 使用缓冲区对象 69 创建缓冲区对象(gl.createBuffer()) 70 绑定缓冲区(gl.bindBuffer()) 71 向缓冲区对象中写入数据(gl.bufferData()) 72 类型化数组 74 将缓冲区对象分配给attribute 变量(gl.vertexAttribPointer()) 75 开启attribute 变量(gl.enableVertexAttribArray()) 77 gl.drawArrays() 的第2 个和第3 个参数 78 用示例程序做实验 79 Hello Triangle 80 示例程序(HelloTriangle.js) 80 基本图形 82 用示例程序做实验 83 Hello Rectangle(HelloQuad) 84 用示例程序做实验 85 移动、旋转和缩放 86 平移 87 示例程序(TranslatedTriangle.js) 88 旋转 91 示例程序(RotatedTriangle.js) 93 变换矩阵:旋转 97 变换矩阵:平移 100 4×4 的旋转矩阵 101 示例程序(RotatedTriangle_Matrix.js) 102 平移:相同的策略 105 变换矩阵:缩放 106 总结 108 第4 章 高级变换与动画基础 109 平移,然后旋转 109 矩阵变换库:cuon-matrix.js 110 示例程序(RotatedTriangle_Matrix4.js) 111 复合变换 113 示例程序(RotatedTranslatedTriangle.js) 115 用示例程序做实验 117 动画 118 动画基础 119 示例程序(RotatingTriangle.js) 119 反复调用绘制函数(tick()) 123 按照指定的旋转角度绘制三角形(draw()) 123 请求再次被调用(requestAnimationFrame()) 125 更新旋转角(animate()) .126 用示例程序做实验 128 总结 130 第5 章 颜色与纹理 131 将非坐标数据传入顶点着色器 131 示例程序(MultiAttributeSize.js) 133 创建多个缓冲区对象 134 gl.vertexAttribPointer() 的步进和偏移参数 135 示例程序(MultiAttributeSize_Interleaved.js) 136 修改颜色(varying 变量) 140 示例程序(MultiAttributeColor.js) 141 用示例程序做实验 144 彩色三角形(ColoredTriangle.js) 145 几何形状的装配和光栅化 145 调用片元着色器 149 用示例程序做实验 149 varying 变量的作用和内插过程 151 在矩形表面贴上图像 153 纹理坐标 156 将纹理图像粘贴到几何图形上 156 示例程序(TexturedQuad.js) 157 设置纹理坐标(initVertexBuffers()) 160 配置和加载纹理(initTextures()) 160 为WebGL 配置纹理(loadTexture()) 164 图像Y 轴反转 164 激活纹理单元(gl.activeTexture()) 165 绑定纹理对象(gl.bindTexture()) 166 配置纹理对象的参数(gl.texParameteri()) 168 将纹理图像分配给纹理对象(gl.texImage2D()) 171 将纹理单元传递给片元着色器(gl.uniform1i()) 173 从顶点着色器向片元着色器传输纹理坐标 174 在片元着色器中获取纹理像素颜色(texture2D()) 174 用示例程序做试验 175 使用多幅纹理 177 示例程序(MultiTexture.js) 178 总结 183 第6 章 OpenGL ES 着色器语言(GLSL ES) 185 回顾:基本着色器代码 186 GLSL ES 概述 186 你好,着色器! 187 基础 187 执行次序 187 注释 187 数据值类型(数值和布尔值) 188 变量 188 GLSL ES 是强类型语言 189 基本类型 189 赋值和类型转换 190 运算符 191 矢量和矩阵 192 赋值和构造 193 访问元素 195 运算符 197 结构体 200 赋值和构造 200 访问成员 200 运算符 201 数组 201 取样器(纹理) 202 运算符优先级 203 程序流程控制:分支和循环 203 if 语句和if-else 语句 203 for 语句 204 continue、break 和discard 语句 205 函数 205 规范声明 207 参数限定词 207 内置函数 208 全局变量和局部变量 209 存储限定字 209 const 变量 209 Attribute 变量 210 uniform 变量 211 varying 变量 211 精度限定字 211 预处理指令 213 总结 215 第7 章 进入三维世界 217 立方体由三角形构成 217 视点和视线 218 视点、观察目标点和上方向 219 示例程序(LookAtTriangles.js) 221 LookAtTriangles.js 与RotatedTriangle_Matrix4.js 224 从指定视点观察旋转后的三角形 225 示例程序(LookAtRotatedTriangles.js) 227 用示例程序做实验 228 利用键盘改变视点 230 示例程序(LookAtTrianglesWithKeys.js) 230 独缺一角 232 可视范围(正射类型) 233 可视空间 234 定义盒状可视空间 235 示例程序(OrthoView.html) 236 示例程序(OrthoView.js) 237 JavaScript 修改HTML 元素 239 顶点着色器的执行流程 239 修改near 和far 值 241 补上缺掉的角(LookAtTrianglesWithKeys_ViewVolume.js) 243 用示例程序做实验 245 可视空间(透视投影) 246 定义透视投影可视空间 247 示例程序(perspectiveview.js) 249 投影矩阵的作用 251 共冶一炉(模型矩阵、视图矩阵和投影矩阵) 252 示例程序(PerspectiveView_mvp.js) 254 用示例程序做实验 257 正确处理对象的前后关系 258 隐藏面消除 260 示例程序(DepthBuffer.js) 262 深度冲突 263 立方体 266 通过顶点索引绘制物体 268 示例程序(HelloCube.js) 268 向缓冲区中写入顶点的坐标、颜色与索引 271 为立方体的每个表面指定颜色 274 示例程序(ColoredCube.js) 275 用示例程序做实验 277 总结 279 第8 章 光照 281 光照原理 281 光源类型 283 反射类型 284 平行光下的漫反射 286 根据光线和表面的方向计算入射角 287 法线:表面的朝向 288 示例程序(LightedCube.js) 291 环境光下的漫反射 296 示例程序(LightedCube_ambient.js) 298 运动物体的光照效果 299 魔法矩阵:逆转置矩阵 301 示例程序(LightedTranslatedRotatedCube.js) 302 点光源光 304 示例程序(PointLightedCube.js) 305 更逼真:逐片元光照 308 示例程序(PointLightedCube_perFragment.js) 309 总结 310 第9 章 层次模型 311 多个简单模型组成的复杂模型 311 层次结构模型 313 单关节模型 314 示例程序(JointMode.js) 315 绘制层次模型(draw()) 319 多节点模型 321 示例程序(MultiJointModel.js) 323 绘制部件(drawBox()) 326 绘制部件(drawSegments()) 327 着色器和着色器程序对象:initShaders() 函数的作用 332 创建着色器对象(gl.createShader()) 333 指定着色器对象的代码(gl.shaderSource()) 334 编译着色器(gl.compileShader()) 334 创建程序对象(gl.createProgram()) 336 为程序对象分配着色器对象(gl.attachShader()) 337 连接程序对象(gl.linkProgram()) 337 告知WebGL 系统所使用的程序对象(gl.useProgram()) 339 initShaders() 函数的内部流程 339 总结 342 第10 章 高级技术 343 用鼠标控制物体旋转 343 如何实现物体旋转 344 示例程序(RotateObject.js) 344 选中物体 347 如何实现选中物体 347 示例程序(PickObject.js) 348 选中一个表面 351 示例程序(PickFace.js) 352 HUD(平视显示器) 355 如何实现HUD 355 示例程序(HUD.html) 356 示例程序(HUD.js) 357 在网页上方显示三维物体 359 雾化(大气效果) 359 如何实现雾化 360 示例程序(Fog.js) 361 使用w 分量(Fog_w.js) 363 绘制圆形的点 364 如何实现圆形的点 364 示例程序(RoundedPoint.js) 366 α 混合 367 如何实现α 混合 367 示例程序(LookAtBlendedTriangles.js) 369 混合函数 369 半透明的三维物体(BlendedCube.js) 371 透明与不透明物体共存 372 切换着色器 373 如何实现切换着色器 374 示例程序(ProgramObject.js) 375 渲染到纹理 379 帧缓冲区对象和渲染缓冲区对象 380 如何实现渲染到纹理 381 示例程序(FramebufferObject.js) 382 创建帧缓冲区对象(gl.createFramebuffer()) 385 创建纹理对象并设置其尺寸和参数 385 创建渲染缓冲区对象(gl.createRenderbuffer()) 386 绑定渲染缓冲区并设置其尺寸(gl.bindRenderbuffer(), gl.renderbufferStorage()) 386 将纹理对象关联到帧缓冲区对象(gl.bindFramebuffer(), gl.framebufferTexture2D()) 388 将渲染缓冲区对象关联到帧缓冲区对象(gl.framebufferRenderbuffer()) 389 检查帧缓冲区的配置(gl.checkFramebufferStatus()) 390 在帧缓冲区进行绘图 390 绘制阴影 392 如何实现阴影 392 示例程序(Shadow.js) 393 提高精度 399 示例程序(Shadow_highp.js) 400 加载三维模型 401 OBJ 文件格式 404 MTL 文件格式 405 示例程序(OBJViewer.js) 406 自定义类型对象 409 示例程序(OBJViewer.js 解析数据部分) 411 响应上下文丢失 418 如何响应上下文丢失 419 示例程序(RotatingTriangle_contextLost.js) 420 总结 422 附录A WebGL 中无须交换缓冲区 423 附录B GLSL ES 1.0 内置函数 427 角度和三角函数 428 指数函数 429 通用函数 430 几何函数 433 矩阵函数 434 矢量函数 435 纹理查询函数 436 附录C 投影矩阵 437 正射投影矩阵 437 透视投影矩阵 437 附录D WebGL/OpenGL :左手还是右手坐标系? 439 示例程序(CoordinateSystem.js) 440 隐藏面消除和裁剪坐标系统 443 裁剪坐标系和可视空间 444 什么是对的? 446 总结 448 附录E 逆转置矩阵 449 附录F 从文件中加载着色器 453 附录G 世界坐标系和本地坐标系 . 457 本地坐标系 458 世界坐标系 459 变换与坐标系 461 附录H WebGL 的浏览器设置 . 463
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值